Android客户端获取服务器的json数据(二)
这一周课很多,所以没有及时的接上。
现在的我不打算再拼命的做码农了,而是尽量的做总结。把以前写过的一些代码回忆一下,把以前有过的思路再重新寻觅一回。似乎,我好多废话。
在做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)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@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接口的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/********* * 启动线程 */ @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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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。在初始化方法里,老规矩先检查网络,然后新建任务,该任务表示获取产品分类。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@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方法。根据编号,执行获取产品分类任务。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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变长字串。再根据字段来解析。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@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设计。