編輯:Android開發實例
經過前面兩篇文章的學習,我們已經掌握了Volley各種Request的使用方法,包括StringRequest、JsonRequest、ImageRequest等。其中StringRequest用於請求一條普通的文本數據,JsonRequest(JsonObjectRequest、JsonArrayRequest)用於請求一條JSON格式的數據,ImageRequest則是用於請求網絡上的一張圖片。
可是Volley提供給我們的Request類型就只有這麼多,而我們都知道,在網絡上傳輸的數據通常有兩種格式,JSON和XML,那麼如果想要請求一條XML格式的數據該怎麼辦呢?其實很簡單,Volley提供了非常強的擴展機制,使得我們可以很輕松地定制出任意類型的Request,這也就是本篇文章的主題了。
在開始之前還是友情提醒一下,如果你還沒有閱讀過我前面兩篇關於Volley的文章,建議先去閱讀一下http://www.fengfly.com/plus/view-215117-1.html和http://www.fengfly.com/plus/view-215118-1.html。
下面我們准備自定義一個XMLRequest,用於請求一條XML格式的數據。那麼該從哪裡開始入手呢?額,好像是有些無從下手。遇到這種情況,我們應該去參考一下Volley的源碼,看一看StringRequest是怎麼實現的,然後就可以模仿著寫出XMLRequest了。首先看下StringRequest的源碼,如下所示:
- /**
- * A canned request for retrieving the response body at a given URL as a String.
- */
- public class StringRequest extends Request<String> {
- private final Listener<String> mListener;
- /**
- * Creates a new request with the given method.
- *
- * @param method the request [email protected] Method} to use
- * @param url URL to fetch the string at
- * @param listener Listener to receive the String response
- * @param errorListener Error listener, or null to ignore errors
- */
- public StringRequest(int method, String url, Listener<String> listener,
- ErrorListener errorListener) {
- super(method, url, errorListener);
- mListener = listener;
- }
- /**
- * Creates a new GET request.
- *
- * @param url URL to fetch the string at
- * @param listener Listener to receive the String response
- * @param errorListener Error listener, or null to ignore errors
- */
- public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
- this(Method.GET, url, listener, errorListener);
- }
- @Override
- protected void deliverResponse(String response) {
- mListener.onResponse(response);
- }
- @Override
- protected Response<String> parseNetworkResponse(NetworkResponse response) {
- String parsed;
- try {
- parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
- } catch (UnsupportedEncodingException e) {
- parsed = new String(response.data);
- }
- return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
- }
- }
可以看到,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了,代碼如下所示:
- public class XMLRequest extends Request<XmlPullParser> {
- private final Listener<XmlPullParser> mListener;
- public XMLRequest(int method, String url, Listener<XmlPullParser> listener,
- ErrorListener errorListener) {
- super(method, url, errorListener);
- mListener = listener;
- }
- public XMLRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorListener) {
- this(Method.GET, url, listener, errorListener);
- }
- @Override
- protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) {
- try {
- String xmlString = new String(response.data,
- HttpHeaderParser.parseCharset(response.headers));
- XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
- XmlPullParser xmlPullParser = factory.newPullParser();
- xmlPullParser.setInput(new StringReader(xmlString));
- return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
- } catch (UnsupportedEncodingException e) {
- return Response.error(new ParseError(e));
- } catch (XmlPullParserException e) {
- return Response.error(new ParseError(e));
- }
- }
- @Override
- protected void deliverResponse(XmlPullParser response) {
- mListener.onResponse(response);
- }
- }
可以看到,其實並沒有什麼太多的邏輯,基本都是仿照StringRequest寫下來的,XMLRequest也是繼承自Request類的,只不過這裡指定的泛型類是XmlPullParser,說明我們准備使用Pull解析的方式來解析XML。在parseNetworkResponse()方法中,先是將服務器響應的數據解析成一個字符串,然後設置到XmlPullParser對象中,在deliverResponse()方法中則是將XmlPullParser對象進行回調。
好了,就是這麼簡單,下面我們嘗試使用這個XMLRequest來請求一段XML格式的數據。http://flash.weather.com.cn/wmaps/xml/china.xml這個接口會將中國所有的省份數據以XML格式進行返回,如下所示:
- <china dn="day" slick-uniqueid="3">
- <city quName="黑龍江" pyName="heilongjiang" cityname="哈爾濱" state1="0" state2="0" stateDetailed="晴" tem1="18" tem2="6" windState="西北風3-4級轉西風小於3級"/>
- <city quName="吉林" pyName="jilin" cityname="長春" state1="0" state2="0" stateDetailed="晴" tem1="19" tem2="6" windState="西北風3-4級轉小於3級"/>
- <city quName="遼寧" pyName="liaoning" cityname="沈陽" state1="0" state2="0" stateDetailed="晴" tem1="21" tem2="7" windState="東北風3-4級"/>
- <city quName="海南" pyName="hainan" cityname="海口" state1="1" state2="1" stateDetailed="多雲" tem1="30" tem2="24" windState="微風"/>
- <city quName="內蒙古" pyName="neimenggu" cityname="呼和浩特" state1="0" state2="0" stateDetailed="晴" tem1="19" tem2="5" windState="東風3-4級"/>
- <city quName="新疆" pyName="xinjiang" cityname="烏魯木齊" state1="0" state2="0" stateDetailed="晴" tem1="22" tem2="10" windState="微風轉東南風小於3級"/>
- <city quName="西藏" pyName="xizang" cityname="拉薩" state1="1" state2="7" stateDetailed="多雲轉小雨" tem1="18" tem2="4" windState="微風"/>
- <city quName="青海" pyName="qinghai" cityname="西寧" state1="0" state2="1" stateDetailed="晴轉多雲" tem1="18" tem2="2" windState="微風"/>
- <city quName="寧夏" pyName="ningxia" cityname="銀川" state1="0" state2="0" stateDetailed="晴" tem1="19" tem2="8" windState="微風"/>
- <city quName="甘肅" pyName="gansu" cityname="蘭州" state1="0" state2="0" stateDetailed="晴" tem1="21" tem2="6" windState="微風"/>
- <city quName="河北" pyName="hebei" cityname="石家莊" state1="0" state2="0" stateDetailed="晴" tem1="25" tem2="12" windState="北風小於3級"/>
- <city quName="河南" pyName="henan" cityname="鄭州" state1="0" state2="0" stateDetailed="晴" tem1="24" tem2="13" windState="微風"/>
- <city quName="湖北" pyName="hubei" cityname="武漢" state1="0" state2="0" stateDetailed="晴" tem1="24" tem2="12" windState="微風"/>
- <city quName="湖南" pyName="hunan" cityname="長沙" state1="2" state2="1" stateDetailed="陰轉多雲" tem1="20" tem2="15" windState="北風小於3級"/>
- <city quName="山東" pyName="shandong" cityname="濟南" state1="1" state2="1" stateDetailed="多雲" tem1="20" tem2="10" windState="北風3-4級轉小於3級"/>
- <city quName="江蘇" pyName="jiangsu" cityname="南京" state1="2" state2="2" stateDetailed="陰" tem1="19" tem2="13" windState="西北風4-5級轉3-4級"/>
- <city quName="安徽" pyName="anhui" cityname="合肥" state1="2" state2="1" stateDetailed="陰轉多雲" tem1="20" tem2="12" windState="西北風轉北風3-4級"/>
- <city quName="山西" pyName="shanxi" cityname="太原" state1="0" state2="0" stateDetailed="晴" tem1="22" tem2="8" windState="微風"/>
- <city quName="陝西" pyName="sanxi" cityname="西安" state1="1" state2="0" stateDetailed="多雲轉晴" tem1="21" tem2="9" windState="東北風小於3級"/>
- <city quName="四川" pyName="sichuan" cityname="成都" state1="1" state2="1" stateDetailed="多雲" tem1="26" tem2="15" windState="南風小於3級"/>
- <city quName="雲南" pyName="yunnan" cityname="昆明" state1="7" state2="7" stateDetailed="小雨" tem1="21" tem2="13" windState="微風"/>
- <city quName="貴州" pyName="guizhou" cityname="貴陽" state1="1" state2="3" stateDetailed="多雲轉陣雨" tem1="21" tem2="11" windState="東風小於3級"/>
- <city quName="浙江" pyName="zhejiang" cityname="杭州" state1="3" state2="1" stateDetailed="陣雨轉多雲" tem1="22" tem2="14" windState="微風"/>
- <city quName="福建" pyName="fujian" cityname="福州" state1="1" state2="2" stateDetailed="多雲轉陰" tem1="28" tem2="18" windState="微風"/>
- <city quName="江西" pyName="jiangxi" cityname="南昌" state1="2" state2="1" stateDetailed="陰轉多雲" tem1="23" tem2="15" windState="北風3-4級轉微風"/>
- <city quName="廣東" pyName="guangdong" cityname="廣州" state1="3" state2="2" stateDetailed="陣雨轉陰" tem1="26" tem2="20" windState="微風"/>
- <city quName="廣西" pyName="guangxi" cityname="南寧" state1="3" state2="3" stateDetailed="陣雨" tem1="23" tem2="19" windState="東北風小於3級"/>
- <city quName="北京" pyName="beijing" cityname="北京" state1="0" state2="0" stateDetailed="晴" tem1="26" tem2="10" windState="微風"/>
- <city quName="天津" pyName="tianjin" cityname="天津" state1="1" state2="0" stateDetailed="多雲轉晴" tem1="22" tem2="13" windState="東北風3-4級轉小於3級"/>
- <city quName="上海" pyName="shanghai" cityname="上海" state1="7" state2="1" stateDetailed="小雨轉多雲" tem1="20" tem2="16" windState="西北風3-4級"/>
- <city quName="重慶" pyName="chongqing" cityname="重慶" state1="1" state2="3" stateDetailed="多雲轉陣雨" tem1="21" tem2="14" windState="微風"/>
- <city quName="香港" pyName="xianggang" cityname="香港" state1="3" state2="1" stateDetailed="陣雨轉多雲" tem1="26" tem2="22" windState="微風"/>
- <city quName="澳門" pyName="aomen" cityname="澳門" state1="3" state2="1" stateDetailed="陣雨轉多雲" tem1="27" tem2="22" windState="東北風3-4級轉微風"/>
- <city quName="台灣" pyName="taiwan" cityname="台北" state1="9" state2="7" stateDetailed="大雨轉小雨" tem1="28" tem2="21" windState="微風"/>
- <city quName="西沙" pyName="xisha" cityname="西沙" state1="3" state2="3" stateDetailed="陣雨" tem1="30" tem2="26" windState="東北風4-5級"/>
- <city quName="南沙" pyName="nanshadao" cityname="南沙" state1="1" state2="1" stateDetailed="多雲" tem1="32" tem2="27" windState="東風4-5級"/>
- <city quName="釣魚島" pyName="diaoyudao" cityname="釣魚島" state1="7" state2="1" stateDetailed="小雨轉多雲" tem1="23" tem2="19" windState="西南風3-4級轉北風5-6級"/>
- </china>
確定了訪問接口後,我們只需要在代碼中按照以下的方式來使用XMLRequest即可:
- XMLRequest xmlRequest = new XMLRequest(
- "http://flash.weather.com.cn/wmaps/xml/china.xml",
- new Response.Listener<XmlPullParser>() {
- @Override
- public void onResponse(XmlPullParser response) {
- try {
- int eventType = response.getEventType();
- while (eventType != XmlPullParser.END_DOCUMENT) {
- switch (eventType) {
- case XmlPullParser.START_TAG:
- String nodeName = response.getName();
- if ("city".equals(nodeName)) {
- String pName = response.getAttributeValue(0);
- Log.d("TAG", "pName is " + pName);
- }
- break;
- }
- eventType = response.next();
- }
- } catch (XmlPullParserException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }, new Response.ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- Log.e("TAG", error.getMessage(), error);
- }
- });
- mQueue.add(xmlRequest);
可以看到,這裡XMLRequest的用法和StringRequest幾乎是一模一樣的,我們先創建出一個XMLRequest的實例,並把服務器接口地址傳入,然後在onResponse()方法中解析響應的XML數據,並把每個省的名字打印出來,最後將這個XMLRequest添加到RequestQueue當中。
現在運行一下代碼,觀察控制台日志,就可以看到每個省的名字都從XML中解析出來了,如下圖所示。
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,代碼如下所示:
- public class GsonRequest<T> extends Request<T> {
- private final Listener<T> mListener;
- private Gson mGson;
- private Class<T> mClass;
- public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,
- ErrorListener errorListener) {
- super(method, url, errorListener);
- mGson = new Gson();
- mClass = clazz;
- mListener = listener;
- }
- public GsonRequest(String url, Class<T> clazz, Listener<T> listener,
- ErrorListener errorListener) {
- this(Method.GET, url, clazz, listener, errorListener);
- }
- @Override
- protected Response<T> parseNetworkResponse(NetworkResponse response) {
- try {
- String jsonString = new String(response.data,
- HttpHeaderParser.parseCharset(response.headers));
- return Response.success(mGson.fromJson(jsonString, mClass),
- HttpHeaderParser.parseCacheHeaders(response));
- } catch (UnsupportedEncodingException e) {
- return Response.error(new ParseError(e));
- }
- }
- @Override
- protected void deliverResponse(T response) {
- mListener.onResponse(response);
- }
- }
可以看到,GsonRequest是繼承自Request類的,並且同樣提供了兩個構造函數。在parseNetworkResponse()方法中,先是將服務器響應的數據解析出來,然後通過調用Gson的fromJson方法將數據組裝成對象。在deliverResponse方法中仍然是將最終的數據進行回調。
那麼下面我們就來測試一下這個GsonRequest能不能夠正常工作吧,調用http://www.weather.com.cn/data/sk/101010100.html這個接口可以得到一段JSON格式的天氣數據,如下所示:
{"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類,代碼如下所示:
- public class Weather {
- private WeatherInfo weatherinfo;
- public WeatherInfo getWeatherinfo() {
- return weatherinfo;
- }
- public void setWeatherinfo(WeatherInfo weatherinfo) {
- this.weatherinfo = weatherinfo;
- }
- }
Weather類中只是引用了WeatherInfo這個類。接著新建WeatherInfo類,代碼如下所示:
- public class WeatherInfo {
- private String city;
- private String temp;
- private String time;
- public String getCity() {
- return city;
- }
- public void setCity(String city) {
- this.city = city;
- }
- public String getTemp() {
- return temp;
- }
- public void setTemp(String temp) {
- this.temp = temp;
- }
- public String getTime() {
- return time;
- }
- public void setTime(String time) {
- this.time = time;
- }
- }
WeatherInfo類中含有city、temp、time這幾個字段。下面就是如何調用GsonRequest了,其實也很簡單,代碼如下所示:
- GsonRequest<Weather> gsonRequest = new GsonRequest<Weather>(
- "http://www.weather.com.cn/data/sk/101010100.html", Weather.class,
- new Response.Listener<Weather>() {
- @Override
- public void onResponse(Weather weather) {
- WeatherInfo weatherInfo = weather.getWeatherinfo();
- Log.d("TAG", "city is " + weatherInfo.getCity());
- Log.d("TAG", "temp is " + weatherInfo.getTemp());
- Log.d("TAG", "time is " + weatherInfo.getTime());
- }
- }, new Response.ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- Log.e("TAG", error.getMessage(), error);
- }
- });
- mQueue.add(gsonRequest);
可以看到,這裡onResponse()方法的回調中直接返回了一個Weather對象,我們通過它就可以得到WeatherInfo對象,接著就能從中取出JSON中的相關數據了。現在運行一下代碼,觀察控制台日志,打印數據如下圖所示:
這樣的話,XMLRequest和GsonRequest的功能就基本都實現了,我們也是借助這兩個例子深刻地理解了自定義Request的方法,對Volley的認識也是更加深入了。好了,本篇文章就到此結束,下篇文章中我們將對Volley進行更深層次的研究,感興趣的朋友請繼續閱讀下一篇。
做個網站的安卓客戶端,用戶安裝到自己手機上,如果我出了新版本怎麼辦呢?要有版本更新功能。 本來版本檢測最好可以自動進行。但如果每次開啟程序,都要先檢測一輪,是一種
工廠方法模式定義: Define an interface for creating an object, but let subclasses decide w
知識點: 這次將繼續上一篇文章沒有講完的Menu的學習,上下文菜單(Context menu)和彈出菜單(Popup menu)。 上下文菜單 上下文菜單提供對U
關於 android 常用布局,利用 XML 文件實現已經有很多的實例了。但如何利用代碼實現呢?當然利用代碼實現沒有太大的必要,也是不提倡的,但我覺得利用代碼實現