最近在參加學校安排的學期實訓,主要的項目是根據實訓需求實現基於WEB的旅游分享平台,並實現對應的Android客戶端(截止到2012-07-16,Android的最高版本是4.1,可以在http://www.android.com/上查看最近更新)。
在開發Android應用平台的時,遇到的第一個比較麻煩的問題就是在Android模擬器中訪問服務器接口。
1. 最先犯的錯誤就是:在連接服務器的時候使用 127.0.0.1 或者 localhost 來指定本機PC機器。 由於Android客戶端在測試的時候是在Android模擬器中運行的,所以127.0.0.1並不會指向PC開發機,而是指向模擬器。 為了在測試的時候能夠訪問到本機PC開發機提供的接口,需要在服務器地址連接中使用 10.0.2.2:8080來指向本機PC機, 或者使用PC機當前的IP地址也可以。如下連接地址:
1 public static String server_url = "http://10.0.2.2:8080/travel";
2. 在Android應用的AndroidManifest.xml中添加網絡權限:
1 <uses-permission android:name="android.permission.INTERNET" />
3. 由於Android 4.1對於網絡限制比較的嚴格,所以在處理網絡連接的時候需要調用以下代碼。 我將其封裝在一個通用的方法中,如下所示:
1 @SuppressLint("NewApi") 2 public static void StrictMode() { 3 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() 4 .detectDiskReads() 5 .detectDiskWrites() 6 .detectNetwork() 7 .penaltyLog() 8 .build()); 9 10 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 11 .detectLeakedSqlLiteObjects() 12 .penaltyLog() 13 .penaltyDeath() 14 .build()); 15 }
4. 在Android客戶端訪問服務器接口的方法有很多,我使用的是 HttpPost來完成服務器請求的。 具體的代碼如下:
1 //網絡通信 2 private HttpPost request; 3 private List<NameValuePair> params; 4 private HttpClient client; 5 private HttpResponse response; 6 private String server_url = Util.server_url + "/servlet/user" ; 7 8 9 //創建線程時,初始化參數 10 params = new ArrayList<NameValuePair>(); 11 params.add(new BasicNameValuePair("action", Util.USER_LOGIN)); 12 params.add(new BasicNameValuePair("username", username)); 13 params.add(new BasicNameValuePair("password", password)); 14 15 16 //處理用戶登錄 17 try{ 18 request = new HttpPost(server_url); 19 request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); 20 21 client = new DefaultHttpClient(); 22 response = client.execute(request); 23 24 if(response.getStatusLine().getStatusCode() == 200) { 25 JSONArray array = new JSONArray(EntityUtils.toString(response.getEntity())); 26 JSONObject result = array.optJSONObject(0); 27 28 if(result.getInt("status") == 0){ 29 USER = result.getJSONObject("user"); 30 login_handler.sendEmptyMessage(LOGIN_SUCCESS); 31 }else{ 32 login_handler.sendEmptyMessage(LOGIN_FAILED); 33 } 34 }else { 35 login_handler.sendEmptyMessage(LOGIN_FAILED); 36 } 37 }catch(Exception e){ 38 login_handler.sendEmptyMessage(LOGIN_EXCEPTION); 39 }finally{ 40 progress.dismiss(); 41 }
這里只是展示了整個發送請求的三個階段:聲明網絡通信API對象、初始化參數並添加到List列表、發送請求並處理請求。
在Android應用中,主要分為UI主線程和子線程。 其中,UI主線程負責屏幕元素的操作,子線程則處理數據的請求,不能對屏幕元素進行操作, 這二者通過Handler 或者繼承子Handler對象的子類對象來完成通信。 一般Handler對象主要使用三個方法: sendEmptyMessage(SUCCESS)、handleMessage(Message msg)。
如上述代碼所示,在子線程處理完數據之后,通過使用sendEmptyMessage方法來返回操作的狀態值,Handler類中的handleMessage會根據返回的狀態值自動對數據進行處理。 一個最簡單的Handler子類的定義如下:
1 class LoginHandler extends Handler{ 2 3 @Override 4 public void handleMessage(Message msg) { 5 // TODO Auto-generated method stub 6 super.handleMessage(msg); 7 8 switch(msg.what){ 9 case LOGIN_SUCCESS: 10 try { 11 onLoginSuccess(); 12 } catch (JSONException e) { 13 e.printStackTrace(); 14 } 15 break; 16 case LOGIN_FAILED: 17 onLoginFailed(); 18 break; 19 case LOGIN_EXCEPTION: 20 onLoginException(); 21 break; 22 } 23 }