zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    redis集群报Jedis does not support password protected Redis Cluster configurations异常解决办法
    redis集群密码设置
    Redis 3.2.4集群实战
    Redis3.2.4 Cluster集群搭建
    redis集群环境的搭建和错误分析
    Centos iptables防火墙关闭启动详解
    动态的表格数据
    ubuntu使用抓包工具,charles
    layer结合art实现弹出功能
    禅道开源版源码安装
  • 原文地址:https://www.cnblogs.com/summers/p/4398679.html
Copyright © 2011-2022 走看看