產品中使用Volley框架已有多時,自己已有良好封裝的Volley確實給程序開發帶來了不少便利與快捷。但隨着產品功能的不斷增長,服務器接口的不斷複雜化,直接使用Volley原生的JSONObjectRequest已經致使Activity或Fragment層中耦合了大量的數據解析代碼,同時當多處調用同一接口時,相似的數據解析代碼還不可複用,致使大量重複代碼的出現,已經讓我愈加地沒法忍受。基於此,最近思考着對Volley原生的JSONObjectRequest(由於產品中目前和服務器交互全部的接口,數據都是json格式的)進行二次封裝,把Activity和Fragment中大量的數據解析代碼剝離出來,同時實現數據解析代碼的複用。php
爲了把問題表現出來,先上一段坑爹的代碼。html
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的代碼是徹底沒法複用的,這不科學java
下面開始分析:android
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進行封裝了,先貼這個類:apache
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的封裝,這是一個抽象類,讓不一樣的子類根據不一樣的接口,趨實現不一樣的數據解析方式:json
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兩個方法都爲抽象方法,讓子類針對不一樣的接口,實現不一樣的功能。api
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封裝了數據解析的代碼,徹底可複用,最後看Activityapp
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