volley二次封装

     产品中使用Volley框架已有多时,本身已有良好封装的Volley确实给程序开发带来了很多便利与快捷。但随着产品功能的不断增加,服务器接口的不断复杂化,直接使用Volley原生的JSONObjectRequest已经导致Activity或Fragment层中耦合了大量的数据解析代码,同时当多处调用同一接口时,类似的数据解析代码还不可复用,导致大量重复代码的出现,已经让我越发地无法忍受。基于此,最近思考着对Volley原生的JSONObjectRequest(因为产品中目前和服务器交互所有的接口,数据都是json格式的)进行二次封装,把Activity和Fragment中大量的数据解析代码剥离出来,同时实现数据解析代码的复用。

为了把问题表现出来,先上一段坑爹的代码。

  1 package com.backup;
  2 import java.util.ArrayList;
  3 import java.util.HashMap;
  4 import java.util.Iterator;
  5 import java.util.List;
  6 import java.util.Map;
  7 
  8 import org.json.JSONException;
  9 import org.json.JSONObject;
 10 
 11 import com.amuro.volleytest01_image.R;
 12 import com.android.volley.RequestQueue;
 13 import com.android.volley.Response;
 14 import com.android.volley.VolleyError;
 15 import com.android.volley.toolbox.JsonObjectRequest;
 16 import com.android.volley.toolbox.Volley;
 17 
 18 import android.app.Activity;
 19 import android.os.Bundle;
 20 import android.view.View;
 21 import android.widget.AdapterView;
 22 import android.widget.AdapterView.OnItemClickListener;
 23 import android.widget.ListView;
 24 import android.widget.SimpleAdapter;
 25 import android.widget.TextView;
 26 
 27 public class TestActivity02 extends Activity
 28 {
 29     private RequestQueue mQueue;
 30     private ListView listView;
 31     private List<Map<String, String>> list = new ArrayList<Map<String,String>>();
 32     
 33     String url = "http://10.24.4.196:8081/weather.html";
 34     
 35     @Override
 36     protected void onCreate(Bundle savedInstanceState)
 37     {
 38         super.onCreate(savedInstanceState);
 39         setContentView(R.layout.activity_test02_layout);
 40         listView = (ListView)findViewById(R.id.lv_test02);
 41         mQueue = Volley.newRequestQueue(this);
 42         getWeatherInfo();
 43         
 44         SimpleAdapter simpleAdapter = new SimpleAdapter(this, list,   
 45                         android.R.layout.simple_list_item_2, new String[] {"title","content"},   
 46                        new int[] {android.R.id.text1, android.R.id.text2});              
 47                   
 48         listView.setAdapter(simpleAdapter);  
 49 
 50         listView.setOnItemClickListener(new OnItemClickListener()
 51         {
 52 
 53             @Override
 54             public void onItemClick(AdapterView<?> parent, View view,
 55                     int position, long id)
 56             {
 57                 TextView tv = (TextView)view.findViewById(android.R.id.text1);
 58                 tv.setText("111111111111111111");
 59             }
 60         });
 61     }
 62     
 63     public void getWeatherInfo()
 64     {
 65         JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null,
 66 
 67         new Response.Listener<JSONObject>()
 68         {
 69 
 70             @SuppressWarnings("unchecked")
 71             @Override
 72             public void onResponse(JSONObject jsonObject)
 73             {
 74                 list.clear();
 75                 Iterator<String> it = jsonObject.keys();
 76                 while (it.hasNext())
 77                 {
 78                     String key = it.next();
 79                     JSONObject obj = null;
 80                     try
 81                     {
 82                         obj = jsonObject.getJSONObject(key);
 83                     }
 84                     catch (JSONException e)
 85                     {
 86                         e.printStackTrace();
 87                     }
 88                     if (obj != null)
 89                     {
 90                         Iterator<String> objIt = obj.keys();
 91                         while (objIt.hasNext())
 92                         {
 93                             String objKey = objIt.next();
 94                             String objValue;
 95                             try
 96                             {
 97                                 objValue = obj.getString(objKey);
 98                                 HashMap<String, String> map = new HashMap<String, String>();
 99                                 map.put("title", objKey);
100                                 map.put("content", objValue);
101                                 list.add(map);
102                             }
103                             catch (JSONException e)
104                             {
105                                 e.printStackTrace();
106                             }
107                         }
108                     }
109                 }
110             }
111         },
112 
113         new Response.ErrorListener()
114         {
115             @Override
116             public void onErrorResponse(VolleyError arg0)
117             {
118             }
119         });
120 
121         mQueue.add(jsonObjectRequest);
122     }
123 }

上面的代码大家可以看到,复杂的json解析代码全部写在Activity里,现在如果又来一个Activity需要调用这个接口,这些解析json的代码是完全无法复用的,这不科学

下面开始分析:

1. 面向对象,对于Activity这层来说,它要的只是拿到数据进行展示,至于数据怎么变出来的,它不应该关注,所以第一件事,对数据进行封装,每个接口返回的最终数据,不应该是一个未经解析的jsonObject,而应该是一个bean,千千万万的bean最终可通过泛型来统一,so,我们先需要一个监听器,让我们封装后的Volley层直接把bean回调给Activity。
2. 对错误的处理,从目前的产品需求来看,上层Activity就是要对不同的错误展示不同的界面或跳转不同的界面,所以我们把错误统一为errorCode和errorMessage,在底层封装好后,直接抛给Activity。所以这样一个返回bean或者error的接口就出来了。
1 package com.amuro.volley_framwork.network_helper;
2 
3 public interface UIDataListener<T>
4 {
5     public void onDataChanged(T data);
6     public void onErrorHappened(String errorCode, String errorMessage);
7 }


3. 好,监听器剥离了Activity与我们的Volley层,下面我们就要自己对Volley的JsonObjectRequest进行封装了,先贴这个类:

  1 package com.amuro.volley_framwork.network_request;
  2 
  3 import java.io.UnsupportedEncodingException;
  4 import java.net.URLEncoder;
  5 import java.util.List;
  6 import java.util.Map;
  7 
  8 import org.apache.http.NameValuePair;
  9 import org.apache.http.client.utils.URLEncodedUtils;
 10 import org.json.JSONObject;
 11 
 12 import com.android.volley.DefaultRetryPolicy;
 13 import com.android.volley.NetworkResponse;
 14 import com.android.volley.ParseError;
 15 import com.android.volley.Response;
 16 import com.android.volley.Response.ErrorListener;
 17 import com.android.volley.Response.Listener;
 18 import com.android.volley.toolbox.HttpHeaderParser;
 19 import com.android.volley.toolbox.JsonRequest;
 20 
 21 public class NetworkRequest extends JsonRequest<JSONObject>
 22 {
 23     private Priority mPriority = Priority.HIGH;
 24     
 25     public NetworkRequest(int method, String url,
 26             Map<String, String> postParams, Listener<JSONObject> listener,
 27             ErrorListener errorListener) 
 28     {
 29         super(method, url, paramstoString(postParams), listener, errorListener);
 30         setRetryPolicy(new DefaultRetryPolicy(30000, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
 31     }
 32 
 33     public NetworkRequest(String url, List<NameValuePair> params,
 34             Listener<JSONObject> listener, ErrorListener errorListener) 
 35     {
 36         this(Method.GET, urlBuilder(url, params), null, listener, errorListener);
 37     }
 38     
 39     public NetworkRequest(String url, Listener<JSONObject> listener, ErrorListener errorListener) 
 40     {
 41         this(Method.GET, url, null, listener, errorListener);
 42     }
 43     
 44     private static String paramstoString(Map<String, String> params)
 45     {
 46         if (params != null && params.size() > 0)
 47         {
 48             String paramsEncoding = "UTF-8";
 49             StringBuilder encodedParams = new StringBuilder();
 50             try
 51             {
 52                 for (Map.Entry<String, String> entry : params.entrySet())
 53                 {
 54                     encodedParams.append(URLEncoder.encode(entry.getKey(),
 55                             paramsEncoding));
 56                     encodedParams.append('=');
 57                     encodedParams.append(URLEncoder.encode(entry.getValue(),
 58                             paramsEncoding));
 59                     encodedParams.append('&');
 60                     
 61                 }
 62                 return encodedParams.toString();
 63             }
 64             catch (UnsupportedEncodingException uee)
 65             {
 66                 throw new RuntimeException("Encoding not supported: "
 67                         + paramsEncoding, uee);
 68             }
 69         }
 70         return null;
 71     }
 72 
 73     @Override
 74     protected Response<JSONObject> parseNetworkResponse(NetworkResponse response)
 75     {
 76         
 77         try
 78         {
 79 
 80             JSONObject jsonObject = new JSONObject(new String(response.data, "UTF-8"));
 81 
 82             return Response.success(jsonObject,
 83                     HttpHeaderParser.parseCacheHeaders(response));
 84 
 85         }
 86         catch (Exception e)
 87         {
 88 
 89             return Response.error(new ParseError(e));
 90 
 91         }
 92     }
 93 
 94     @Override
 95     public Priority getPriority()
 96     {
 97         return mPriority;
 98     }
 99 
100     public void setPriority(Priority priority)
101     {
102         mPriority = priority;
103     }
104 
105     private static String urlBuilder(String url, List<NameValuePair> params) 
106     {
107         return url + "?" + URLEncodedUtils.format(params, "UTF-8");
108     }
109 }

 

4. 接下来就是我们的重头戏,写一个Controller来操作这个request,同时对数据进行bean或error的封装,这是一个抽象类,让不同的子类根据不同的接口,趋实现不同的数据解析方式:

  1 package com.amuro.volley_framwork.network_helper;
  2 
  3 import java.util.List;
  4 import java.util.Map;
  5 
  6 import org.apache.http.NameValuePair;
  7 import org.json.JSONObject;
  8 
  9 import android.content.Context;
 10 import android.util.Log;
 11 
 12 import com.amuro.volley_framwork.network_request.NetworkRequest;
 13 import com.amuro.volley_framwork.volley_queue_controller.VolleyQueueController;
 14 import com.android.volley.Request.Method;
 15 import com.android.volley.Response;
 16 import com.android.volley.Response.ErrorListener;
 17 import com.android.volley.VolleyError;
 18 
 19 public abstract class NetworkHelper<T> implements Response.Listener<JSONObject>, ErrorListener
 20 {
 21     private Context context;
 22     
 23     public NetworkHelper(Context context)
 24     {
 25         this.context = context;
 26     }
 27     
 28     protected Context getContext()
 29     {
 30         return context;
 31     }
 32     
 33     protected NetworkRequest getRequestForGet(String url, List<NameValuePair> params)
 34     {
 35         if(params == null)
 36         {
 37             return new NetworkRequest(url, this, this);
 38         }
 39         else
 40         {
 41             return new NetworkRequest(url, params, this, this);
 42         }
 43         
 44     }
 45     
 46     protected NetworkRequest getRequestForPost(String url, Map<String, String> params)
 47     {
 48         return new NetworkRequest(Method.POST, url, params, this, this);
 49     }
 50     
 51     public void sendGETRequest(String url, List<NameValuePair> params)
 52     {
 53         VolleyQueueController.getInstance().
 54             getRequestQueue(getContext()).add(getRequestForGet(url, params));
 55     }
 56     
 57     public void sendPostRequest(String url, Map<String, String> params)
 58     {
 59         VolleyQueueController.getInstance().
 60             getRequestQueue(context).add(getRequestForPost(url, params));
 61     }
 62 
 63     @Override
 64     public void onErrorResponse(VolleyError error)
 65     {
 66         Log.d("Amuro", error.getMessage());
 67         disposeVolleyError(error);
 68     }
 69 
 70     protected abstract void disposeVolleyError(VolleyError error);
 71 
 72     @Override
 73     public void onResponse(JSONObject response)
 74     {
 75         Log.d("Amuro", response.toString());
 76         disposeResponse(response);
 77     }
 78 
 79     protected abstract void disposeResponse(JSONObject response);
 80 
 81     private UIDataListener<T> uiDataListener;
 82     
 83     public void setUiDataListener(UIDataListener<T> uiDataListener)
 84     {
 85         this.uiDataListener = uiDataListener;
 86     }
 87     
 88     protected void notifyDataChanged(T data)
 89     {
 90         if(uiDataListener != null)
 91         {
 92             uiDataListener.onDataChanged(data);
 93         }
 94     }
 95     
 96     protected void notifyErrorHappened(String errorCode, String errorMessage)
 97     {
 98         if(uiDataListener != null)
 99         {
100             uiDataListener.onErrorHappened(errorCode, errorMessage);
101         }
102     }
103     
104 }

这里对外直接提供了sendGetRequest方法和sendPostRequest方法,做为api就是要清晰明了,不要让调用者去了解还有Method.GET这样的东西,同时getRequestForGet方法和getRequestForPost方法把最常用的request直接封装好,不需要子类再去写new request的代码。当然为了拓展,这两个方法是protected的,default的request不能符合要求的时候,子类就可直接覆盖这两个方法返回自己的request,而disposeResponse和disponseError两个方法都为抽象方法,让子类针对不同的接口,实现不同的功能。

5. 下面来个子类实例,一看就懂。

 1 package com.amuro.controller.networkhelper;
 2 
 3 import org.json.JSONObject;
 4 
 5 import android.content.Context;
 6 
 7 import com.amuro.bean.RRBean;
 8 import com.amuro.utils.SystemParams;
 9 import com.amuro.volley_framwork.network_helper.NetworkHelper;
10 import com.android.volley.VolleyError;
11 
12 //{"errorCode":"0000","errorMessage":"成功","respMsg":"success","success":"true"}
13 public class ReverseRegisterNetworkHelper extends NetworkHelper<RRBean>
14 {
15     
16 
17     public ReverseRegisterNetworkHelper(Context context)
18     {
19         super(context);
20     }
21 
22     @Override
23     protected void disposeVolleyError(VolleyError error)
24     {
25         notifyErrorHappened(
26                 SystemParams.VOLLEY_ERROR_CODE, 
27                 error == null ? "NULL" : error.getMessage());
28     }
29 
30     @Override
31     protected void disposeResponse(JSONObject response)
32     {
33         RRBean rrBean = null;
34         
35         if(response != null)
36         {
37             try
38             {
39                 String errorCode = response.getString("errorCode");
40                 String errorMessage = response.getString("errorMessage");
41                 String respMsg = response.getString("respMsg");
42                 String success = response.getString("success");
43                 
44                 if("0000".equals(errorCode))
45                 {
46                     rrBean = new RRBean();
47                     rrBean.setErrorCode(errorCode);
48                     rrBean.setErrorMessage(errorMessage);
49                     rrBean.setRespMsg(respMsg);
50                     rrBean.setSuccess(success);
51                     
52                     notifyDataChanged(rrBean);
53                 }
54                 else
55                 {
56                     notifyErrorHappened(errorCode, errorMessage);
57                 }
58             }
59             catch(Exception e)
60             {
61                 notifyErrorHappened(SystemParams.RESPONSE_FORMAT_ERROR, "Response format error");
62             }
63         }
64         else
65         {
66             notifyErrorHappened(SystemParams.RESPONSE_IS_NULL, "Response is null!");
67         }
68         
69     }
70     
71     
72 
73 }

 

5. 大功告成,这个NetworkHelper封装了数据解析的代码,完全可复用,最后看Activity

 1 package com.amuro.ui;
 2 
 3 import com.amuro.bean.RRBean;
 4 import com.amuro.controller.networkhelper.ReverseRegisterNetworkHelper;
 5 import com.amuro.utils.SystemParams;
 6 import com.amuro.volley_framwork.network_helper.NetworkHelper;
 7 import com.amuro.volley_framwork.network_helper.UIDataListener;
 8 import com.amuro.volleytest01_image.R;
 9 
10 import android.app.Activity;
11 import android.os.Bundle;
12 import android.view.View;
13 import android.view.View.OnClickListener;
14 import android.widget.Button;
15 import android.widget.Toast;
16 
17 public class MyVolleyTestActivity extends Activity implements UIDataListener<RRBean>
18 {
19     private Button button;
20     
21     private NetworkHelper<RRBean> networkHelper;
22     
23     @Override
24     protected void onCreate(Bundle savedInstanceState)
25     {
26         super.onCreate(savedInstanceState);
27         setContentView(R.layout.activity_my_volley_test_layout);
28         
29         networkHelper = new ReverseRegisterNetworkHelper(this);
30         networkHelper.setUiDataListener(this);
31         
32         button = (Button)findViewById(R.id.bt);
33         button.setOnClickListener(new OnClickListener()
34         {
35             
36             @Override
37             public void onClick(View v)
38             {
39                 sendRequest();
40             }
41         });
42     }
43     
44     private void sendRequest()
45     {
46         networkHelper.sendGETRequest(SystemParams.TEST_URL, null);
47     }
48 
49     @Override
50     public void onDataChanged(RRBean data)
51     {
52         Toast.makeText(
53                 this, 
54                 data.getErrorCode() + ":" + 
55                 data.getErrorMessage() + ":" + 
56                 data.getRespMsg() + ":" + 
57                 data.getSuccess(), 
58                 Toast.LENGTH_SHORT).show();
59         
60     }
61 
62     @Override
63     public void onErrorHappened(String errorCode, String errorMessage)
64     {
65         Toast.makeText(
66                 this, 
67                 errorCode + ":" + errorMessage, 
68                 Toast.LENGTH_SHORT).show();
69         
70     }
71 }

 

看,Activity直接拿到的就是数据或者errorCode,把一大堆复杂的数据解析代码剥离了。

转自:http://www.aichengxu.com/view/46975

posted @ 2015-04-07 16:36  Summers  阅读(13983)  评论(11编辑  收藏  举报