這一周課很多,所以沒有及時的接上。
現在的我不打算再拼命的做碼農了,而是盡量的做總結。把以前寫過的一些代碼回憶一下,把以前有過的思路再重新尋覓一回。似乎,我好多廢話。
在做Android應用程序開發時,有很多應用都會獲取由Web Server返回的數據,有的可能是xml數據,有可能是json數據。他們各有應用范圍。我繼續總結一下獲取網絡json數據的一些idea。這里先分析一下業務邏輯,UI稍后再嘮叨。
1.分析一下手機購物應用的執行過程。
首次執行一個MainActivity,此activity的功能是構成main UI(即下方有個Tab 菜單),由於我的沒個Activity都是繼承了上文中的IMActivity接口,這里實現初始化init()方法。我們都知道Activity的生命周期,所以我在onResume()方法里調用init()方法。並且,初始化方法完成幾個任務。
1.1 檢查網絡連接
android系統有提供檢測網絡的api。我們可以很方便的調用。我們可以把檢查網絡的方法封裝在一個工具類里(可根據自己的programing style)

package com.mpros.util; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; /*** * 工具類,檢查當前網絡狀態 * * @author shuimu * */ public class NetUtil { public static boolean checkNet(Context context) { // 獲取手機所以連接管理對象(包括wi-fi,net等連接的管理) ConnectivityManager conn = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (conn != null) { // 網絡管理連接對象 NetworkInfo info = conn.getActiveNetworkInfo(); if(info != null && info.isConnected()) { // 判斷當前網絡是否連接 if (info.getState() == NetworkInfo.State.CONNECTED) { return true; } } } return false; } }
1.2如果網絡連接正常,啟動服務Service。否則提醒用戶網絡連接異常。網絡連接異常的方法,我封裝在了MainService中,可以直接invoke。

@Override protected void onResume() { super.onResume(); init(); } @Override public void init() { if (NetUtil.checkNet(this)) { Intent startService = new Intent("com.mpros.service.MainService"); this.startService(startService); } else { MainService.alerNetErr(this); } }
2.當MainActivity執行到onResume()方法時,會啟動服務。正常情況下,MainService開始執行onCreate()方法。此方法里,啟動線程,因為我的MainService是實現了Runnbale接口的。

public class MainService extends Service implements Runnable { 。。。 // 循環控制變量 private boolean isrun = true; 。。。 @Override public void onCreate() { super.onCreate(); isrun = true; new Thread(this).start(); } }
3.於是,后台服務框架正常搭建起來了。子線程一直在獲取任務,只要有任務的時候,取走任務,然后就執行任務。
run方法:

/********* * 啟動線程 */ @Override public void run() { while (isrun) { Task lastTask = null; if (allTasks.size() > 0) { synchronized (allTasks) { // 獲取任務 lastTask = allTasks.get(0); // 執行任務 doTask(lastTask); } } // 如果沒有任務,則等待2000ms,繼續獲取任務 try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } }
4.插敘一下Handler。
在每個Acitivity和Service主線程下,有一個Handler對象負責線程通信,消息傳遞,更新UI。(我是這么理解的)。通常采用異步實現網絡通信,通過Handler來做數據媒介,然后把數據顯示在界面上來。
主要涉及Message對象和handMessage(Message msg)方法。有時候還常用Handler對象的postXXX(Runnable run)方法。此方法有幾個重載方法。就是加入一個Runnable(api:Represents a command that can be executed,表示可執行的命令)到Handler對象的附屬子線程隊列里。
5.理一下doTask(Task task)方法。
5.1先瞧瞧系統要執行的任務Task

package com.mpros.bean; import java.util.Map; /** * 任務類 獲取不同信息 * * @author Scherrer * */ public class Task { // 任務編號 public static final int TASK_GET_PRODUCTTYPE = 0;// 獲取產品類別 public static final int GET_TYPE_LOGO = 1; // 獲取父類logo public static final int TASK_GET_CHILDTYPE_LOGO = 2;// 獲取二級類別logo public static final int TASK_GET_PRODUCT_IMAGE = 3;// 獲取產品 // 日志 public static final String Logger = "logger"; private int taskId;// 任務編號 @SuppressWarnings("rawtypes") private Map taskParam;// 任務參數 @SuppressWarnings("rawtypes") public Task(int taskId, Map taskParam) { this.taskId = taskId; this.taskParam = taskParam; } public int getTaskId() { return taskId; } public void setTaskId(int taskId) { this.taskId = taskId; } @SuppressWarnings("rawtypes") public Map getTaskParam() { return taskParam; } @SuppressWarnings("rawtypes") public void setTaskParam(Map taskParam) { this.taskParam = taskParam; } }
這里我聲明成了原生類型了,但在Java里,在使用HashMap或者Map,常常會涉及泛型,如:Map<?,?> map ;
5.2 首先創建一個消息對象
Message msg = new Message();
5.3 賦予消息的標識,這里把任務編號賦予它。
msg.what = task.getTaskId();
5.4 通過switch語句,根據任務ID分別執行任務。具體代碼上文已貼了。
6.在點擊獲取產品分類時,會轉至相應分類的Activity。在初始化方法里,老規矩先檢查網絡,然后新建任務,該任務表示獲取產品分類。

@Override public void init() { // 網絡正常,新建獲取產品類別的任務 if (NetUtil.checkNet(this)) { Task getTypeTask = new Task(Task.TASK_GET_PRODUCTTYPE, null); MainService.newTask(getTypeTask); // 將此activity入棧 MainService.allActivities.add(this); } else { MainService.alerNetErr(this); } }
7.MainService會馬上收到任務,然后調用doTask方法。根據編號,執行獲取產品分類任務。

Message msg = new Message(); System.out.println("任務編號: " + task.getTaskId()); msg.what = task.getTaskId(); try { switch (task.getTaskId()) { case Task.TASK_GET_PRODUCTTYPE:// 獲取產品類型
8.封裝通過Http獲取請求響應(HttpResponse)以及通過一個圖片的URL,然后返回一張位圖(BitmapDrawable)的工具類HttpUtil.

package com.mpros.util; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import android.graphics.drawable.BitmapDrawable; public class HttpUtil { public static HttpResponse getHttpResponse(String url) throws ClientProtocolException, IOException { // post請求 HttpPost post = new HttpPost(url); // 請求客戶 DefaultHttpClient client = new DefaultHttpClient(); return client.execute(post); } /*** * 獲取logo * * @param url * @return */ public static BitmapDrawable getImageFromUrl(String action) { BitmapDrawable icon = null; try { URL url = new URL(action); HttpURLConnection hc = (HttpURLConnection) url.openConnection(); icon = new BitmapDrawable(hc.getInputStream()); hc.disconnect(); } catch (Exception e) { } return icon; } }
9.解析json數據的業務。
通過HttpUtil獲取一個HttpResponse.
HttpResponse response = HttpUtil.getHttpResponse(action);//在瀏覽器里輸入此action,瀏覽器里可顯示一串json數據。
測試響應碼
// 響應code
int rescode = response.getStatusLine().getStatusCode();如果rescode 為200,表示獲取ok.
然后通過response.getEntity().getContent();方法返回獲取的所有數據,在轉化為StringBuffer變長字串。再根據字段來解析。

package com.mpros.service.json; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpResponse; import org.json.JSONArray; import org.json.JSONObject; import android.util.Log; import com.mpros.bean.Task; import com.mpros.bean.product.Product; import com.mpros.bean.product.ProductBrand; import com.mpros.bean.product.ProductStyle; import com.mpros.bean.product.ProductType; import com.mpros.bean.product.Sex; import com.mpros.util.HttpUtil; /*** * 產品模塊 解析json數據 * * @author Scherrer * */ public class ProductTypeService { /** * 獲取產品類型 * * @param action * 訪問URl * @return 類型列表 */ public static List<ProductType> getTypesFromJson(String action) { List<ProductType> types = null; try { HttpResponse response = HttpUtil.getHttpResponse(action); // 響應code int rescode = response.getStatusLine().getStatusCode(); if (rescode == 200) {// 獲取ok String line = null; StringBuilder builder = new StringBuilder();// 可變String BufferedReader lineReader = new BufferedReader( new InputStreamReader(response.getEntity().getContent())); while ((line = lineReader.readLine()) != null) { builder.append(line); } Log.i(Task.Logger, builder.toString()); JSONObject obj = new JSONObject(builder.toString()); obj = obj.getJSONObject("types"); // 以關鍵字types獲取一個json數組 JSONArray array = obj.getJSONArray("results"); // /System.out.println("json array " + array.toString()); types = new ArrayList<ProductType>(); JSONObject temp = null; for (int i = 0; i < array.length(); ++i) { temp = (JSONObject) array.opt(i); System.out.println("temp i" + temp.toString()); ProductType type = resolveProductType(temp); // 是否有子類 if (temp.getString("childTypes") != null) { JSONArray array2 = temp.getJSONArray("childTypes"); System.out.println("獲取的子類數組 -- " + array2.toString()); List<ProductType> childtypes = new ArrayList<ProductType>(); for (int e = 0; e < array2.length(); ++e) { JSONObject temp2 = (JSONObject) array2.opt(e); System.out.println("二級類別 一條記錄 ---- " + temp2.toString()); ProductType childtype = resolveProduct(temp2); if (temp.getString("childTypes") != null) { JSONArray array3 = temp .getJSONArray("childTypes"); List<ProductType> child2types = new ArrayList<ProductType>(); for (int k = 0; k < array3.length(); ++k) { JSONObject temp3 = (JSONObject) array3 .opt(k); System.out.println("temp3 k" + temp3.toString()); child2types.add(resolveProduct(temp3)); } childtype.setChildtypes(child2types); } if(childtype != null) { childtypes.add(childtype); } } type.setChildtypes(childtypes); } if (type != null) { types.add(type); } } } } catch (Exception e) { e.printStackTrace(); return null; } Log.i(Task.Logger, "types --- 大小" + types.size()); for (ProductType type : types) { if (type.getChildtypes() != null) { Log.i(Task.Logger, "type child types --- 大小" + type.getChildtypes().size()); } } return types != null ? types : null; } public static ProductType resolveProduct(JSONObject temp) { ProductType type = new ProductType(); try { // 獲取type的簡單屬性 System.out.println("產品類型屬性:"); type.setTypeid(Integer.parseInt(temp.getString("typeId"))); System.out.println("typeid: " + Integer.parseInt(temp.getString("typeId"))); type.setName(temp.getString("name")); System.out.println("type name -- " + temp.getString("name")); type.setRemark(temp.getString("remark")); System.out.println("type remark -- " + temp.getString("remark")); type.setTypelogo(temp.getString("typelogo")); JSONArray products = temp.getJSONArray("products"); if (products != null) { System.out.println("類別產品 -- " + products.toString()); // 獲取type的所有產品--產品列表 for (int j = 0; j < products.length(); ++j) { JSONObject p = (JSONObject) products.opt(j); Product product = new Product(); System.out.println("一個產品記錄 -- " + p.toString()); // 產品簡單屬性 System.out.println("產品id-- " + p.getInt("id")); product.setId(p.getInt("id")); product.setCode(p.getString("code")); System.out.println("產品貨號: " + p.getString("code")); product.setBaseprice(Float.parseFloat(p .getString("baseprice"))); if (p.getString("buyexplain") != null) { product.setBuyexplain(p.getString("buyexplain")); } product.setClickcount(p.getInt("clickcount")); product.setCommend(p.getBoolean("commend")); product.setCreatetime(p.getString("createdate")); product.setDescription(p.getString("description")); product.setMarketprice(Float.parseFloat(p .getString("marketprice"))); if(p.getString("model") != null) { product.setModel(p.getString("model")); } product.setName(p.getString("name")); product.setSellcount(p.getInt("sellcount")); product.setSellprice(Float.parseFloat(p .getString("sellprice"))); product.setSexrequest(Sex.valueOf(p.getString("sexrequest"))); System.out.println("產品性別要求 -- " + product.getSexrequest()); /*if (p.getString("weight") != null) { product.setWeight(Integer.parseInt(p.getString("weight"))); }*/ System.out.println(" 產品品牌 測試 ---------"); // 品牌 /*if (p.getString("brand") != null) { JSONObject b = p.getJSONObject("brand"); System.out.println("產品品牌記錄 -- " + b.toString()); ProductBrand brand = new ProductBrand(); brand.setId(b.getString("id")); brand.setLogoPath(b.getString("logoPath")); brand.setName(b.getString("name")); // 產品添加品牌 product.setBrand(brand); }*/ System.out.println(" 產品樣式 測試 ---------"); if (p.getJSONArray("styles") != null) { // 產品樣式 JSONArray ss = p.getJSONArray("styles"); System.out.println("產品樣式記錄 -- " + ss.toString()); for (int k = 0; k < ss.length(); ++k) { JSONObject s = (JSONObject) ss.opt(k); System.out.println("一條樣式記錄 -- " + s.toString()); ProductStyle style = new ProductStyle(); style.setId(s.getInt("id")); style.setName(s.getString("name")); style.setImagename(s.getString("imagename")); // 添加樣式 product.addStyle(style); } } // 添加產品 type.addProduct(product); } } } catch (Exception e) { e.printStackTrace(); return null; } return type; } /*** * 解析產品類型 * @return */ public static ProductType resolveProductType(JSONObject temp) { ProductType type = new ProductType(); try { // 獲取type的簡單屬性 System.out.println("產品類型屬性:"); type.setTypeid(Integer.parseInt(temp.getString("typeId"))); System.out.println("typeid: " + Integer.parseInt(temp.getString("typeId"))); type.setName(temp.getString("name")); System.out.println("type name -- " + temp.getString("name")); type.setRemark(temp.getString("remark")); System.out.println("type remark -- " + temp.getString("remark")); type.setTypelogo(temp.getString("typelogo")); System.out.println("type logo -- " + temp.getString("typelogo")); }catch (Exception e) { e.printStackTrace(); return null; } return type; } }
10.在執行獲取產品類型時,正常的話會返回解析好的產品類型對象列表list。然后通過handler對象傳遞該list,根據任務id去更新UI。

msg.obj = types; 。。。 handler.sendMessage(msg); allTasks.remove(task);// 執行完任務,則移出該任務 // 當前服務的子線程Handler,負責處理更新UI操作 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(Task.Logger, "UI 更新編號:" + msg.what); switch (msg.what) { case Task.TASK_GET_PRODUCTTYPE: IMActivity ia = (ClassifyActivity) getActivityByName("ClassifyActivity"); ia.refresh(ClassifyActivity.GET_TYPE_SUCCESS, msg.obj);
11.在ClassifyActivity里實現refresh方法。

@SuppressWarnings("unchecked") @Override public void refresh(Object... param) { switch (((Integer) param[0]).intValue()) { case GET_TYPE_SUCCESS: viewBar.setVisibility(View.GONE); List<ProductType> producttypes = (List<ProductType>) param[1]; this.types = producttypes; if (this.types != null) { System.out.println("獲取的類別記錄 -- " + this.types.size()); Log.i(Task.Logger, "types.size() -- " + this.types.size()); // 適配器 MyListViewAdapter adapter = new MyListViewAdapter(this, this.types); typesListView.setAdapter(adapter); } else { makeToast("加載數據失敗,請再試.."); } break;
12.執行一個任務的邏輯順序就是這樣了,
通過UI新建任務 ---> 后台服務獲取任務並執行任務 ---> 通過相應的方法獲取的數據 ---> Hanlder傳遞數據 ---> 返回原來的UI ---> UI是否更新.
有時間會再補充我覺得比較有用的UI設計。