Android與人人網連接實例:我在聽

 目前,社交網絡概念正火。而手機最初設計的目的正是讓人們進行通信。人人網作為中國最大的社交網站,用戶數量眾多,本文通過一個簡單的小程序:“我在聽”向大傢展示renren api的使用。
首先介紹一下“我在聽”。功能:在用戶聽歌時,在不需要用戶進行額外操作的情況下,根據用戶正在聽的曲目,以發狀態的形式同步至人人網。在安裝完“我在聽”之後,點擊使用人人網登錄,輸入賬號密碼,登錄成功後,選擇是否自動同步,如果此時用戶打開瞭網絡,那麼隻要用戶通過自帶的播放器聽歌,就會自動發佈狀態,例如:我在聽張國榮的《倩女幽魂》。
       下面開始介紹開發過程;
首先,在人人 api頁面http://dev.renren.com/ 裡先登錄,然後創建一個android應用。填寫完表單,創建完成後,可以獲得人人給你的唯一標志:
應用ID:xxxxxxx
API Key:xxxxxxxxxxxxxxxxxxxxxxx
Secret Key:xxxxxxxxxxxxxxxxxxxxxx
這三串字符串用於標志你的應用。
 
下面介紹如何獲取用戶當前正在聽的歌的信息:
當系統默認的播放器開始播放下一首歌時,會發出一個廣播(intent中包含歌曲名,藝術傢等信息),我們隻要定義一個接收這個廣播的廣播接收器,並且從intent中抽取出需要的信息即可。
 
AndroidManifest.xml
[html] view plaincopyprint?
1. <span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?> 
2. <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
3.     package="com.renoqiu" 
4.     android:versionCode="1" 
5.     android:versionName="1.0" > 
6.     <uses-sdk android:minSdkVersion="10" /> 
7.     <uses-permission android:name="android.permission.INTERNET" /> 
8.     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> 
9.     <application 
10.         android:icon="@drawable/ic_launcher" 
11.         android:label="@string/app_name" > 
12.         <activity 
13.             android:name=".IamListenActivity" 
14.             android:label="@string/app_name" > 
15.         </activity> 
16.         <activity 
17.             android:name=".SettingActivity" 
18.             android:label="@string/app_name" > 
19.             <intent-filter> 
20.                 <action android:name="android.intent.action.MAIN" /> 
21.                 <category android:name="android.intent.category.LAUNCHER" /> 
22.             </intent-filter> 
23.         </activity> 
24.         <receiver android:name=".MusicBroadcastReceiver"> 
25.             <intent-filter> 
26.                 <action android:name="com.android.music.metachanged"></action> 
27.             </intent-filter>             
28.         </receiver> 
29.         <service android:name=".PushStatusService" > 
30.             <intent-filter> 
31.                 <action android:name="com.renoqiu.pushstatus" /> 
32.             </intent-filter> 
33.         </service> 
34.     </application> 
35. </manifest> 
36. </span> 
 
根據main.xml可知,我們定義瞭類MusicBroadcastReceiver捕捉action名為com.android.music.metachanged的廣播。
 
src/com/renoqiu/ MusicBroadcastReceiver.java

[java] view plaincopyprint?
1. <span style="font-size:16px;">package com.renoqiu; 
2.  
3. import android.content.BroadcastReceiver; 
4. import android.content.Context; 
5. import android.content.Intent; 
6. import android.os.Bundle; 
7.  
8. public class MusicBroadcastReceiver extends BroadcastReceiver { 
9.     private static final Object SMSRECEIVED = "com.android.music.metachanged"; 
10.     @Override 
11.     public void onReceive(Context context, Intent intent) { 
12.         if(intent.getAction().equals(SMSRECEIVED)){ 
13.             String trackName=intent.getStringExtra("track"); 
14.             String artist=intent.getStringExtra("artist"); 
15.  
16.             Intent pushStatusIntent = new Intent(); 
17.             pushStatusIntent.setAction("com.renoqiu.pushstatus"); 
18.             Bundle myBundle = new Bundle(); 
19.             myBundle.putString("trackName", trackName); 
20.             myBundle.putString("artist", artist); 
21.             pushStatusIntent.putExtras(myBundle); 
22.             context.startService(pushStatusIntent); 
23.         } 
24.     } 
25. } 
26. </span> 
 
在類MusicBroadcastReceiver中我們抽取瞭歌曲名和藝術傢名,並且調用context.startService()方法創建瞭一個service。
 
src/com/renoqiu/ PushStatusService.java

[java] view plaincopyprint?
1. <span style="font-size:16px;">package com.renoqiu; 
2.  
3. import android.app.Service; 
4. import android.content.Context; 
5. import android.content.Intent; 
6. import android.content.SharedPreferences; 
7. import android.net.ConnectivityManager; 
8. import android.net.NetworkInfo; 
9. import android.os.Bundle; 
10. import android.os.Handler; 
11. import android.os.IBinder; 
12. import android.os.Message; 
13. import android.widget.Toast; 
14.  
15. public class PushStatusService extends Service { 
16.     private Handler handler; 
17.     private com.renoqiu.LooperThread thread; 
18.     private SharedPreferences sharedPreferences; 
19.     private boolean syncFlag; 
20.     private ConnectivityManager cm; 
21.     private NetworkInfo ni; 
22.  
23.     @Override 
24.     public IBinder onBind(Intent arg0) { 
25.         return null; 
26.     } 
27.     private boolean checkNet() { 
28.         cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); 
29.         if (cm == null) { 
30.             return false; 
31.         } 
32.         ni = cm.getActiveNetworkInfo(); 
33.         if (ni == null || !ni.isAvailable()) {      
34.             return false;      
35.         }      
36.         return true;      
37.     }     
38.     @Override 
39.     public void onStart(Intent intent, int startId) { 
40.         super.onStart(intent, startId); 
41.          
42.         syncFlag = sharedPreferences.getBoolean("syncFlag",false); 
43.         if(syncFlag && checkNet()){ 
44.             if(intent != null){ 
45.                 Bundle myBundle = intent.getExtras(); 
46.                 String trackName = myBundle.getString("trackName"); 
47.                 String artist = myBundle.getString("artist"); 
48.                 String accessToken = sharedPreferences.getString("accessToken",""); 
49.                  
50.                 if(accessToken != null && !accessToken.equals("")){ 
51.                     String requestMethod = "status.set"; 
52.                     //接口名稱    
53.                     String url = StatusPublishHelper.API_URL; 
54.                     String secretKey = StatusPublishHelper.SECRET_KEY; 
55.                     if(artist == null || artist.equals("")){ 
56.                         artist = "xxx"; 
57.                     } 
58.                     if(trackName == null || trackName.equals("")){ 
59.                         trackName = "xxx"; 
60.                     } 
61.                     String message = "我在聽" + artist + "的《" + trackName + "》。\r\n 通過我在聽發佈!"; 
62.                     thread = new LooperThread(handler, requestMethod, "1.0", url, accessToken, message, secretKey); 
63.                     thread.start(); /* 啟動線程 */ 
64.                 }else{ 
65.                     Toast.makeText(PushStatusService.this, "請登陸!", Toast.LENGTH_SHORT).show(); 
66.                     SharedPreferences.Editor editor = sharedPreferences.edit(); 
67.                     editor.putBoolean("syncFlag", false); 
68.                     editor.commit(); 
69.                 } 
70.             } 
71.         } 
72.     } 
73.  
74.     @Override 
75.     public void onCreate() { 
76.         super.onCreate(); 
77.         sharedPreferences = getSharedPreferences("shared", MODE_PRIVATE); 
78.          
79.         handler = new Handler() { 
80.             @Override 
81.             public void handleMessage(Message msg) { 
82.                 switch (msg.what) { 
83.                     case 0: 
84.                         if((Integer)msg.obj != 1){ 
85.                             Toast.makeText(PushStatusService.this, "同步失敗!請檢查網絡是否打開…", Toast.LENGTH_SHORT).show(); 
86.                         }else{ 
87.                             Toast.makeText(PushStatusService.this, "同步成功!", Toast.LENGTH_SHORT).show(); 
88.                         } 
89.                         break; 
90.                 } 
91.             } 
92.         }; 
93.     } 
94. } 
95. </span> 
 
在PushStatusService中,首先進行一系列檢查,例如:網絡是否已經打開,用戶是否已經登錄,是否開啟同步等信息。其中用到瞭sharedPreferences保存信息。如果條件都滿足那麼將新建一個線程去通過人人api發狀態,其中就需要使用到之前創建應用時所得到的key,在介紹具體如何狀態之前先接著介紹一下人人的api。要通過人人發送狀態首先必須通過人人的登錄認證:OAuth2.0,詳情見:http://wiki.dev.renren.com/wiki/Authentication
我們的登錄流程開始於通過內嵌在IamListenActivity中的Webkit訪問人人OAuth 2.0的Authorize Endpoint:
https://graph.renren.com/oauth/authorize?client_id=YOUR_API_KEY&response_type=token&redirect_uri=YOUR_CALLBACK_URL&display=touch&scope=status_update。
client_id:必須參數。在開發者中心註冊應用時獲得的API Key。
response_type:必須參數。客戶端流程,此值固定為“token”。當用戶登錄成功,瀏覽器會被重定向到YOUR_CALLBACK_URL,並且帶有參數Access Token。這個Access Token可以標志登錄用戶,避免需要多次輸入用戶名密碼。此處我們會把accesstoken保存在sharedPreferences中,下次需要使用時,直接從sharedPreferences中獲取。
redirect_uri:登錄成功,流程結束後要跳轉回得URL。redirect_uri所在的域名必須在開發者中心註冊應用後,填寫在編輯屬性選項卡中填寫到服務器域名中,人人OAuth2.0用以檢查跳轉的合法性。
如果用戶已經登錄,人人OAuth 2.0會校驗存儲在用戶瀏覽器中的Cookie。如果用戶沒有登錄,人人OAuth 2.0會為用戶展示登錄頁面,讓用戶輸入用戶名和密碼:
display=touch:一般的智能手機都是這個選項,
scope=status_update:表示我們會用到更新狀態的功能。
 
src/com/renoqiu/ IamListenActivity.java

[java] view plaincopyprint?
1. <span style="font-size:16px;">package com.renoqiu; 
2.  
3. import java.net.URLDecoder; 
4. import android.app.Activity; 
5. import android.content.SharedPreferences; 
6. import android.os.Bundle; 
7. import android.webkit.WebSettings; 
8. import android.webkit.WebView; 
9. import android.webkit.WebViewClient; 
10. import android.widget.Toast; 
11.  
12. public class IamListenActivity extends Activity { 
13.     private WebView webView; 
14.     private String accessToken = null;   
15.     public void onCreate(Bundle savedInstanceState) { 
16.         super.onCreate(savedInstanceState); 
17.         setContentView(R.layout.main); 
18.         webView = (WebView) findViewById(R.id.web); 
19.         WebSettings settings = webView.getSettings();   
20.         settings.setJavaScriptEnabled(true);   
21.         settings.setSupportZoom(true);   
22.         settings.setBuiltInZoomControls(true);   
23.         webView.loadUrl(StatusPublishHelper.AUTHURL); 
24.         webView.requestFocusFromTouch();   
25.         WebViewClient wvc = new WebViewClient() {   
26.             @Override   
27.             public void onPageFinished(WebView view, String url) {   
28.                 super.onPageFinished(view, url);   
29.                 //人人網用戶名和密碼驗證通過後,刷新頁面時即可返回accessToken    
30.                 String reUrl = webView.getUrl();   
31.                 if (reUrl != null && reUrl.indexOf("access_token") != -1) {   
32.                     //截取url中的accessToken    
33.                     int startPos = reUrl.indexOf("token=") + 6;   
34.                     int endPos = reUrl.indexOf("&expires_in");   
35.                     accessToken = URLDecoder.decode(reUrl.substring(startPos, endPos)); 
36.                     //保存獲取到的accessToken    
37.                     //share.saveRenrenToken(accessToken);    
38.                     Toast.makeText(IamListenActivity.this, "驗證成功,設置同步後。\n聽歌時就能自動傳狀態哦。:)", Toast.LENGTH_SHORT).show(); 
39.                     SharedPreferences settings = (SharedPreferences)getSharedPreferences("shared", MODE_PRIVATE); 
40.                     SharedPreferences.Editor editor = settings.edit(); 
41.                     editor.putString("accessToken", accessToken); 
42.                     editor.putBoolean("syncFlag", false); 
43.                     editor.commit(); 
44.                     finish(); 
45.                 } 
46.             }   
47.         }; 
48.         webView.setWebViewClient(wvc); 
49.     } 
50. } 
51. </span> 
 
res/layout/main.xml

[html] view plaincopyprint?
1. <span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?> 
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
3.     android:layout_width="fill_parent" 
4.     android:layout_height="fill_parent" 
5.     android:orientation="vertical" > 
6.  
7.     <WebView 
8.         android:id="@+id/web" 
9.         android:layout_width="match_parent" 
10.         android:layout_height="match_parent" /> 
11. </LinearLayout> 
12. </span> 
 
由代碼可知,我們通過webkit訪問Authorize Endpoint後,從返回的鏈接中抽取瞭accesstoken,並且保存起來瞭。有瞭access token之後,我們就可以通過renren的api,進行更新狀態的操作瞭,下面的LooperThread就是用於更新狀態,並且給出用戶反饋的類。
“為瞭確保應用與人人API 服務器之間的安全通信,防止Secret Key盜用,數據篡改等惡意攻擊,人人API服務器使用瞭簽名機制(即sig參數)來認證應用。簽名是由請求參數和應用的私鑰Secret Key經過MD5加密後生成的字符串。應用在調用人人API之前,要計算出簽名,並追加到請求參數中。“(關於簽名的計算規則參見:http://wiki.dev.renren.com/wiki/Calculate_signature)
 
 
src/com/renoqiu/ LooperThread.java
[java] view plaincopyprint?
1. <span style="font-size:16px;">package com.renoqiu; 
2.  
3. import java.util.ArrayList; 
4. import java.util.List; 
5.  
6. import org.apache.http.HttpResponse; 
7. import org.apache.http.NameValuePair; 
8. import org.apache.http.client.entity.UrlEncodedFormEntity; 
9. import org.apache.http.client.methods.HttpPost; 
10. import org.apache.http.impl.client.DefaultHttpClient; 
11. import org.apache.http.message.BasicNameValuePair; 
12. import org.apache.http.protocol.HTTP; 
13. import org.apache.http.util.EntityUtils; 
14. import org.json.JSONObject; 
15.  
16. import android.os.Handler; 
17. import android.os.Message; 
18. import android.util.Log; 
19.  
20. public class LooperThread extends Thread{ 
21.     private String requestMethod; 
22.     private String v; 
23.     private String url; 
24.     private String accessToken; 
25.     private String status; 
26.     private String secretKey; 
27.     private Handler fatherHandler; 
28.     public LooperThread(Handler fatherHandler, String requestMethod, String v, String url, String accessToken, String message, String secretKey) { 
29.         this.requestMethod = requestMethod; 
30.         this.v = v; 
31.         this.url = url; 
32.         this.accessToken = accessToken; 
33.         this.status = message;  
34.         this.secretKey = secretKey; 
35.         this.fatherHandler = fatherHandler; 
36.     } 
37.  
38.     public void run() { 
39.         Message msg = new Message(); 
40.         msg.obj = updateStatus(status); 
41.         msg.what = 0; 
42.         fatherHandler.sendMessage(msg); 
43.     } 
44.     public  int updateStatus(String status) {   
45.         int success = 0; 
46.         //生成簽名 字典序排列  
47.         StringBuilder sb = new StringBuilder();   
48.         sb.append("access_token=").append(accessToken)   
49.             .append("format=").append("JSON")   
50.             .append("method=").append(requestMethod)   
51.             .append("status=").append(status)   
52.             .append("v=").append(v) 
53.             .append(secretKey); 
54.         String sig = StatusPublishHelper.getMD5(sb.toString());   
55.  
56.         HttpPost httpRequest = new HttpPost(url); 
57.         List<NameValuePair> params = new ArrayList<NameValuePair>(); 
58.         params.add(new BasicNameValuePair("access_token", accessToken)); 
59.         params.add(new BasicNameValuePair("method", requestMethod));  
60.         params.add(new BasicNameValuePair("v", v));  
61.         params.add(new BasicNameValuePair("status", status));  
62.         params.add(new BasicNameValuePair("format", "JSON")); 
63.         params.add(new BasicNameValuePair("sig", sig)); 
64.          try {  
65.             httpRequest.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); 
66.             HttpResponse httpResponse = new DefaultHttpClient().execute(httpRequest); 
67.             if (httpResponse.getStatusLine().getStatusCode() == 200){ 
68.                 String result = EntityUtils.toString(httpResponse .getEntity()); 
69.                  JSONObject json = new JSONObject(result); 
70.                  success = (Integer)json.get("result"); 
71.                  Log.v("org.reno", result); 
72.             } 
73.         }catch (Exception e){ 
74.             return success; 
75.         }  
76.    
77.         return success;   
78.     } 
79. } 
80. </span> 
 
上面代碼中,首先計算出所有參數以字典序升序排列後,拼接在一起後的md5值作為簽名。然後向http://api.renren.com/restserver.do發送post請求,其中包括之前獲得的access_token,所調用的方法名(此處我們調用的是更新狀態的方法名,關於各種api詳見:http://wiki.dev.renren.com/wiki/API),及方法的參數,此處包括版本,狀態內容,返回類型(此處為json),最後是簽名。
params.add(newBasicNameValuePair("access_token", accessToken));
              params.add(newBasicNameValuePair("method", requestMethod));
              params.add(newBasicNameValuePair("v", v));
              params.add(newBasicNameValuePair("status", status));
              params.add(newBasicNameValuePair("format", "JSON"));
              params.add(newBasicNameValuePair("sig", sig));
然後等待服務器的回復,並且解析json格式的數據,判斷是否發送成功。
到此,基本的發狀態的過程就結束瞭。
下面的類用於提供用戶登陸以及讓用戶選擇是否開啟自動同步。
src/com/renoqiu/ SettingActivity.java

[java] view plaincopyprint?
1. <span style="font-size:16px;">package com.renoqiu; 
2.  
3. import android.app.Activity; 
4. import android.content.Intent; 
5. import android.content.SharedPreferences; 
6. import android.os.Bundle; 
7. import android.view.View; 
8. import android.view.View.OnClickListener; 
9. import android.widget.Button; 
10. import android.widget.CompoundButton; 
11. import android.widget.Toast; 
12. import android.widget.CompoundButton.OnCheckedChangeListener; 
13. import android.widget.ToggleButton; 
14.  
15. public class SettingActivity extends Activity { 
16.     private ToggleButton syncToggleButton; 
17.     private Button loginBtn; 
18.     private SharedPreferences sharedPreferences; 
19.     @Override 
20.     protected void onCreate(Bundle savedInstanceState) { 
21.         super.onCreate(savedInstanceState); 
22.         setContentView(R.layout.setting); 
23.         syncToggleButton = (ToggleButton)findViewById(R.id.syncToggleButton); 
24.         sharedPreferences = (SharedPreferences)getSharedPreferences("shared", MODE_PRIVATE); 
25.         boolean syncFlag = sharedPreferences.getBoolean("syncFlag",false); 
26.         syncToggleButton.setChecked(syncFlag); 
27.         syncToggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener(){ 
28.             @Override 
29.             public void onCheckedChanged(CompoundButton buttonView, 
30.                     boolean isChecked) { 
31.                 if(isChecked == false){ 
32.                     SharedPreferences.Editor editor = sharedPreferences.edit(); 
33.                     editor.putBoolean("syncFlag", isChecked); 
34.                     editor.commit(); 
35.                 }else{ 
36.                     String accessToken = sharedPreferences.getString("accessToken",""); 
37.                     if(accessToken != null && !accessToken.equals("") ){ 
38.                         SharedPreferences.Editor editor = sharedPreferences.edit(); 
39.                         editor.putBoolean("syncFlag", isChecked); 
40.                         editor.commit(); 
41.                     }else{ 
42.                         syncToggleButton.setChecked(false); 
43.                         Toast.makeText(SettingActivity.this, "請先登陸!", Toast.LENGTH_SHORT).show(); 
44.                     } 
45.                      
46.                 } 
47.             } 
48.         }); 
49.         loginBtn = (Button)findViewById(R.id.loginBtn); 
50.         loginBtn.setOnClickListener(new OnClickListener(){ 
51.             @Override 
52.             public void onClick(View arg0) { 
53.                 Intent intent = new Intent(SettingActivity.this, IamListenActivity.class); 
54.                 startActivity(intent); 
55.             }}); 
56.     } 
57. } 
58. </span> 
 
res/layout/setting.xml

[html] view plaincopyprint?
1. <span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?> 
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
3.     android:layout_width="fill_parent" 
4.     android:layout_height="fill_parent" 
5.     android:orientation="vertical" > 
6.         <Button 
7.         android:id="@+id/loginBtn" 
8.         android:layout_width="wrap_content" 
9.         android:layout_height="wrap_content" 
10.         android:text=""  
11.         android:background="@drawable/btn_login" 
12.         android:layout_marginTop="10dp"/> 
13.  
14.     <RelativeLayout 
15.         android:id="@+id/relativeLayout1" 
16.         android:layout_width="match_parent" 
17.         android:layout_height="wrap_content" > 
18.  
19.         <TextView 
20.             android:id="@+id/toggleTextView" 
21.             android:layout_width="wrap_content" 
22.             android:layout_height="wrap_content" 
23.             android:layout_alignParentLeft="true" 
24.             android:layout_alignParentTop="true" 
25.             android:layout_marginTop="20dp" 
26.             android:text="@string/toggleSync" /> 
27.  
28.         <ToggleButton 
29.             android:id="@+id/syncToggleButton" 
30.             android:layout_width="wrap_content" 
31.             android:layout_height="wrap_content" 
32.             android:layout_alignParentTop="true" 
33.             android:layout_marginTop="10dp" 
34.             android:layout_marginLeft="10dp" 
35.             android:layout_toRightOf="@+id/toggleTextView" /> 
36.  
37.     </RelativeLayout> 
38.  
39. </LinearLayout> 
40. </span> 
 
最後一個類包含瞭各種鏈接常量,和把字符串轉換為md5的方法:
src/com/renoqiu/ StatusPublishHelper.java

[java] view plaincopyprint?
1. <span style="font-size:16px;">package com.renoqiu; 
2.  
3. import java.security.MessageDigest; 
4.  
5. public class StatusPublishHelper { 
6.     // 你的應用ID  
7.     public static final String APP_ID = "xxxxx"; 
8.     // 應用的API Key  
9.     public static final String API_KEY = "xxxxxxxxxxxxxxxxxxxxxxx"; 
10.     // 應用的Secret Key  
11.     public static final String SECRET_KEY = "xxxxxxxxxxxxxxxx"; 
12.      
13.     public static final String API_URL = "http://api.renren.com/restserver.do"; 
14.     public static final String AUTHURL = "https://graph.renren.com/oauth/authorize?client_id="   
15.             + API_KEY +"&response_type=token"   
16.             + "&redirect_uri=http://www.renoqiu.com/iamlisten.html&display=touch"   
17.             + "&scope=status_update";   
18.     public static String getMD5(String s) {   
19.         try {   
20.             MessageDigest md5 = MessageDigest.getInstance("MD5");   
21.    
22.             byte[] byteArray = s.getBytes("UTF-8");   
23.             byte[] md5Bytes = md5.digest(byteArray);   
24.    
25.             StringBuffer hexValue = new StringBuffer();   
26.    
27.             for (int i = 0; i < md5Bytes.length; i++) {   
28.                 int val = ((int) md5Bytes[i]) & 0xff;   
29.                 if (val < 16)   
30.                     hexValue.append("0");   
31.                 hexValue.append(Integer.toHexString(val));   
32.             }   
33.    
34.             return hexValue.toString();   
35.    
36.         } catch (Exception e) {   
37.             e.printStackTrace();   
38.             return null;   
39.         } 
40.     } 
41. } 
42. </span> 
 
下圖為測試使用的效果。

   
  
 

 
SourceCode下載鏈接:
https://github.com/renoqiu/IamListening
需要註意的是:下載的源代碼並不能直接使用,讀者需要自行修改IamListening/src/com/renoqiu/StatusPublishHelper.java類下的APP_ID, API_KEY, SECRET_KEY 這三個常量為你申請的應用的對應的值後,就可以正常使用瞭。
http://code.google.com/p/iamlisten/downloads/list可以從這裡現在一個筆者編譯好的apk文件,進行測試。

摘自 北京大學-Google Android實驗室

 
 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。