Android Volley完全解析(三),定製自己的Request

轉載請註明出處:http://blog.csdn.net/guolin_blog/article/details/17612763

經過前面兩篇文章的學習,我們已經掌握了Volley各種Request的使用方法,包括StringRequest、JsonRequest、ImageRequest等。其中StringRequest用於請求一條普通的文本數據,JsonRequest(JsonObjectRequest、JsonArrayRequest)用於請求一條JSON格式的數據,ImageRequest則是用於請求網絡上的一張圖片。

可是Volley提供給我們的Request類型就只有這麼多,而我們都知道,在網絡上傳輸的數據通常有兩種格式,JSON和XML,那麼如果想要請求一條XML格式的數據該怎麼辦呢?其實很簡單,Volley提供了非常強的擴展機制,使得我們可以很輕鬆地定製出任意類型的Request,這也就是本篇文章的主題了。

在開始之前還是友情提醒一下,如果你還沒有閱讀過我前面兩篇關於Volley的文章,建議先去閱讀一下Android Volley完全解析(一),初識Volley的基本用法Android Volley完全解析(二),使用Volley加載網絡圖片

1. 自定義XMLRequest

下面我們準備自定義一個XMLRequest,用於請求一條XML格式的數據。那麼該從哪裏開始入手呢?額,好像是有些無從下手。遇到這種情況,我們應該去參考一下Volley的源碼,看一看StringRequest是怎麼實現的,然後就可以模仿着寫出XMLRequest了。首先看下StringRequest的源碼,如下所示:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. /** 

  2.  * A canned request for retrieving the response body at a given URL as a String. 

  3.  */  

  4. public class StringRequest extends Request<String> {  

  5.     private final Listener<String> mListener;  

  6.   

  7.     /** 

  8.      * Creates a new request with the given method. 

  9.      * 

  10.      * @param method the request {@link Method} to use 

  11.      * @param url URL to fetch the string at 

  12.      * @param listener Listener to receive the String response 

  13.      * @param errorListener Error listener, or null to ignore errors 

  14.      */  

  15.     public StringRequest(int method, String url, Listener<String> listener,  

  16.             ErrorListener errorListener) {  

  17.         super(method, url, errorListener);  

  18.         mListener = listener;  

  19.     }  

  20.   

  21.     /** 

  22.      * Creates a new GET request. 

  23.      * 

  24.      * @param url URL to fetch the string at 

  25.      * @param listener Listener to receive the String response 

  26.      * @param errorListener Error listener, or null to ignore errors 

  27.      */  

  28.     public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {  

  29.         this(Method.GET, url, listener, errorListener);  

  30.     }  

  31.   

  32.     @Override  

  33.     protected void deliverResponse(String response) {  

  34.         mListener.onResponse(response);  

  35.     }  

  36.   

  37.     @Override  

  38.     protected Response<String> parseNetworkResponse(NetworkResponse response) {  

  39.         String parsed;  

  40.         try {  

  41.             parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));  

  42.         } catch (UnsupportedEncodingException e) {  

  43.             parsed = new String(response.data);  

  44.         }  

  45.         return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));  

  46.     }  

  47. }  

可以看到,StringRequest的源碼很簡練,根本就沒幾行代碼,我們一起來分析下。首先StringRequest是繼承自Request類的,Request可以指定一個泛型類,這裏指定的當然就是String了,接下來StringRequest中提供了兩個有參的構造函數,參數包括請求類型,請求地址,以及響應回調等,由於我們已經很熟悉StringRequest的用法了,相信這幾個參數的作用都不用再解釋了吧。但需要注意的是,在構造函數中一定要調用super()方法將這幾個參數傳給父類,因爲HTTP的請求和響應都是在父類中自動處理的。

另外,由於Request類中的deliverResponse()和parseNetworkResponse()是兩個抽象方法,因此StringRequest中需要對這兩個方法進行實現。deliverResponse()方法中的實現很簡單,僅僅是調用了mListener中的onResponse()方法,並將response內容傳入即可,這樣就可以將服務器響應的數據進行回調了。parseNetworkResponse()方法中則應該對服務器響應的數據進行解析,其中數據是以字節的形式存放在NetworkResponse的data變量中的,這裏將數據取出然後組裝成一個String,並傳入Response的success()方法中即可。

瞭解了StringRequest的實現原理,下面我們就可以動手來嘗試實現一下XMLRequest了,代碼如下所示:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. public class XMLRequest extends Request<XmlPullParser> {  

  2.   

  3.     private final Listener<XmlPullParser> mListener;  

  4.   

  5.     public XMLRequest(int method, String url, Listener<XmlPullParser> listener,  

  6.             ErrorListener errorListener) {  

  7.         super(method, url, errorListener);  

  8.         mListener = listener;  

  9.     }  

  10.   

  11.     public XMLRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorListener) {  

  12.         this(Method.GET, url, listener, errorListener);  

  13.     }  

  14.   

  15.     @Override  

  16.     protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {  

  17.         try {  

  18.             String xmlString = new String(response.data,  

  19.                     HttpHeaderParser.parseCharset(response.headers));  

  20.             XmlPullParserFactory factory = XmlPullParserFactory.newInstance();  

  21.             XmlPullParser xmlPullParser = factory.newPullParser();  

  22.             xmlPullParser.setInput(new StringReader(xmlString));  

  23.             return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));  

  24.         } catch (UnsupportedEncodingException e) {  

  25.             return Response.error(new ParseError(e));  

  26.         } catch (XmlPullParserException e) {  

  27.             return Response.error(new ParseError(e));  

  28.         }  

  29.     }  

  30.   

  31.     @Override  

  32.     protected void deliverResponse(XmlPullParser response) {  

  33.         mListener.onResponse(response);  

  34.     }  

  35.   

  36. }  

可以看到,其實並沒有什麼太多的邏輯,基本都是仿照StringRequest寫下來的,XMLRequest也是繼承自Request類的,只不過這裏指定的泛型類是XmlPullParser,說明我們準備使用Pull解析的方式來解析XML。在parseNetworkResponse()方法中,先是將服務器響應的數據解析成一個字符串,然後設置到XmlPullParser對象中,在deliverResponse()方法中則是將XmlPullParser對象進行回調。

好了,就是這麼簡單,下面我們嘗試使用這個XMLRequest來請求一段XML格式的數據。http://flash.weather.com.cn/wmaps/xml/china.xml這個接口會將中國所有的省份數據以XML格式進行返回,如下所示:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. <china dn="day" slick-uniqueid="3">  

  2. <city quName="黑龍江" pyName="heilongjiang" cityname="哈爾濱" state1="0" state2="0" stateDetailed="晴" tem1="18" tem2="6" windState="西北風3-4級轉西風小於3級"/>  

  3. <city quName="吉林" pyName="jilin" cityname="長春" state1="0" state2="0" stateDetailed="晴" tem1="19" tem2="6" windState="西北風3-4級轉小於3級"/>  

  4. <city quName="遼寧" pyName="liaoning" cityname="瀋陽" state1="0" state2="0" stateDetailed="晴" tem1="21" tem2="7" windState="東北風3-4級"/>  

  5. <city quName="海南" pyName="hainan" cityname="海口" state1="1" state2="1" stateDetailed="多雲" tem1="30" tem2="24" windState="微風"/>  

  6. <city quName="內蒙古" pyName="neimenggu" cityname="呼和浩特" state1="0" state2="0" stateDetailed="晴" tem1="19" tem2="5" windState="東風3-4級"/>  

  7. <city quName="新疆" pyName="xinjiang" cityname="烏魯木齊" state1="0" state2="0" stateDetailed="晴" tem1="22" tem2="10" windState="微風轉東南風小於3級"/>  

  8. <city quName="西藏" pyName="xizang" cityname="拉薩" state1="1" state2="7" stateDetailed="多雲轉小雨" tem1="18" tem2="4" windState="微風"/>  

  9. <city quName="青海" pyName="qinghai" cityname="西寧" state1="0" state2="1" stateDetailed="晴轉多雲" tem1="18" tem2="2" windState="微風"/>  

  10. </china>  

確定了訪問接口後,我們只需要在代碼中按照以下的方式來使用XMLRequest即可:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. XMLRequest xmlRequest = new XMLRequest(  

  2.         "http://flash.weather.com.cn/wmaps/xml/china.xml",  

  3.         new Response.Listener<XmlPullParser>() {  

  4.             @Override  

  5.             public void onResponse(XmlPullParser response) {  

  6.                 try {  

  7.                     int eventType = response.getEventType();  

  8.                     while (eventType != XmlPullParser.END_DOCUMENT) {  

  9.                         switch (eventType) {  

  10.                         case XmlPullParser.START_TAG:  

  11.                             String nodeName = response.getName();  

  12.                             if ("city".equals(nodeName)) {  

  13.                                 String pName = response.getAttributeValue(0);  

  14.                                 Log.d("TAG""pName is " + pName);  

  15.                             }  

  16.                             break;  

  17.                         }  

  18.                         eventType = response.next();  

  19.                     }  

  20.                 } catch (XmlPullParserException e) {  

  21.                     e.printStackTrace();  

  22.                 } catch (IOException e) {  

  23.                     e.printStackTrace();  

  24.                 }  

  25.             }  

  26.         }, new Response.ErrorListener() {  

  27.             @Override  

  28.             public void onErrorResponse(VolleyError error) {  

  29.                 Log.e("TAG", error.getMessage(), error);  

  30.             }  

  31.         });  

  32. mQueue.add(xmlRequest);  

可以看到,這裏XMLRequest的用法和StringRequest幾乎是一模一樣的,我們先創建出一個XMLRequest的實例,並把服務器接口地址傳入,然後在onResponse()方法中解析響應的XML數據,並把每個省的名字打印出來,最後將這個XMLRequest添加到RequestQueue當中。


現在運行一下代碼,觀察控制檯日誌,就可以看到每個省的名字都從XML中解析出來了,如下圖所示。

2. 自定義GsonRequest

JsonRequest的數據解析是利用Android本身自帶的JSONObject和JSONArray來實現的,配合使用JSONObject和JSONArray就可以解析出任意格式的JSON數據。不過也許你會覺得使用JSONObject還是太麻煩了,還有很多方法可以讓JSON數據解析變得更加簡單,比如說GSON。遺憾的是,Volley中默認並不支持使用自家的GSON來解析數據,不過沒有關係,通過上面的學習,相信你已經知道了自定義一個Request是多麼的簡單,那麼下面我們就來舉一反三一下,自定義一個GsonRequest。

首先我們需要把gson的jar包添加到項目當中,jar包的下載地址是:https://code.google.com/p/google-gson/downloads/list 。

接着定義一個GsonRequest繼承自Request,代碼如下所示:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. public class GsonRequest<T> extends Request<T> {  

  2.   

  3.     private final Listener<T> mListener;  

  4.   

  5.     private Gson mGson;  

  6.   

  7.     private Class<T> mClass;  

  8.   

  9.     public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,  

  10.             ErrorListener errorListener) {  

  11.         super(method, url, errorListener);  

  12.         mGson = new Gson();  

  13.         mClass = clazz;  

  14.         mListener = listener;  

  15.     }  

  16.   

  17.     public GsonRequest(String url, Class<T> clazz, Listener<T> listener,  

  18.             ErrorListener errorListener) {  

  19.         this(Method.GET, url, clazz, listener, errorListener);  

  20.     }  

  21.   

  22.     @Override  

  23.     protected Response<T> parseNetworkResponse(NetworkResponse response) {  

  24.         try {  

  25.             String jsonString = new String(response.data,  

  26.                     HttpHeaderParser.parseCharset(response.headers));  

  27.             return Response.success(mGson.fromJson(jsonString, mClass),  

  28.                     HttpHeaderParser.parseCacheHeaders(response));  

  29.         } catch (UnsupportedEncodingException e) {  

  30.             return Response.error(new ParseError(e));  

  31.         }  

  32.     }  

  33.   

  34.     @Override  

  35.     protected void deliverResponse(T response) {  

  36.         mListener.onResponse(response);  

  37.     }  

  38.   

  39. }  

可以看到,GsonRequest是繼承自Request類的,並且同樣提供了兩個構造函數。在parseNetworkResponse()方法中,先是將服務器響應的數據解析出來,然後通過調用Gson的fromJson方法將數據組裝成對象。在deliverResponse方法中仍然是將最終的數據進行回調。


那麼下面我們就來測試一下這個GsonRequest能不能夠正常工作吧,調用http://www.weather.com.cn/data/sk/101010100.html這個接口可以得到一段JSON格式的天氣數據,如下所示:

[plain] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. {"weatherinfo":{"city":"北京","cityid":"101010100","temp":"19","WD":"南風","WS":"2級","SD":"43%","WSE":"2","time":"19:45","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB"}}  

接下來我們使用對象的方式將這段JSON字符串表示出來。新建一個Weather類,代碼如下所示:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. public class Weather {  

  2.   

  3.     private WeatherInfo weatherinfo;  

  4.   

  5.     public WeatherInfo getWeatherinfo() {  

  6.         return weatherinfo;  

  7.     }  

  8.   

  9.     public void setWeatherinfo(WeatherInfo weatherinfo) {  

  10.         this.weatherinfo = weatherinfo;  

  11.     }  

  12.   

  13. }  

Weather類中只是引用了WeatherInfo這個類。接着新建WeatherInfo類,代碼如下所示:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. public class WeatherInfo {  

  2.   

  3.     private String city;  

  4.   

  5.     private String temp;  

  6.   

  7.     private String time;  

  8.   

  9.     public String getCity() {  

  10.         return city;  

  11.     }  

  12.   

  13.     public void setCity(String city) {  

  14.         this.city = city;  

  15.     }  

  16.   

  17.     public String getTemp() {  

  18.         return temp;  

  19.     }  

  20.   

  21.     public void setTemp(String temp) {  

  22.         this.temp = temp;  

  23.     }  

  24.   

  25.     public String getTime() {  

  26.         return time;  

  27.     }  

  28.   

  29.     public void setTime(String time) {  

  30.         this.time = time;  

  31.     }  

  32.   

  33. }  

WeatherInfo類中含有city、temp、time這幾個字段。下面就是如何調用GsonRequest了,其實也很簡單,代碼如下所示:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片

  1. GsonRequest<Weather> gsonRequest = new GsonRequest<Weather>(  

  2.         "http://www.weather.com.cn/data/sk/101010100.html", Weather.class,  

  3.         new Response.Listener<Weather>() {  

  4.             @Override  

  5.             public void onResponse(Weather weather) {  

  6.                 WeatherInfo weatherInfo = weather.getWeatherinfo();  

  7.                 Log.d("TAG""city is " + weatherInfo.getCity());  

  8.                 Log.d("TAG""temp is " + weatherInfo.getTemp());  

  9.                 Log.d("TAG""time is " + weatherInfo.getTime());  

  10.             }  

  11.         }, new Response.ErrorListener() {  

  12.             @Override  

  13.             public void onErrorResponse(VolleyError error) {  

  14.                 Log.e("TAG", error.getMessage(), error);  

  15.             }  

  16.         });  

  17. mQueue.add(gsonRequest);  

可以看到,這裏onResponse()方法的回調中直接返回了一個Weather對象,我們通過它就可以得到WeatherInfo對象,接着就能從中取出JSON中的相關數據了。現在運行一下代碼,觀察控制檯日誌,打印數據如下圖所示:


這樣的話,XMLRequest和GsonRequest的功能就基本都實現了,我們也是藉助這兩個例子深刻地理解了自定義Request的方法,對Volley的認識也是更加深入了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章