转载请注明出处: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 plain copy
  1. /**
  2. *AcannedrequestforretrievingtheresponsebodyatagivenURLasaString.
  3. */
  4. publicclassStringRequestextendsRequest<String>{
  5. privatefinalListener<String>mListener;
  6. /**
  7. *Createsanewrequestwiththegivenmethod.
  8. *
  9. *@parammethodtherequest{@linkMethod}touse
  10. *@paramurlURLtofetchthestringat
  11. *@paramlistenerListenertoreceivetheStringresponse
  12. *@paramerrorListenerErrorlistener,ornulltoignoreerrors
  13. */
  14. publicStringRequest(intmethod,Stringurl,Listener<String>listener,
  15. ErrorListenererrorListener){
  16. super(method,url,errorListener);
  17. mListener=listener;
  18. }
  19. /**
  20. *CreatesanewGETrequest.
  21. *
  22. *@paramurlURLtofetchthestringat
  23. *@paramlistenerListenertoreceivetheStringresponse
  24. *@paramerrorListenerErrorlistener,ornulltoignoreerrors
  25. */
  26. publicStringRequest(Stringurl,Listener<String>listener,ErrorListenererrorListener){
  27. this(Method.GET,url,listener,errorListener);
  28. }
  29. @Override
  30. protectedvoiddeliverResponse(Stringresponse){
  31. mListener.onResponse(response);
  32. }
  33. @Override
  34. protectedResponse<String>parseNetworkResponse(NetworkResponseresponse){
  35. Stringparsed;
  36. try{
  37. parsed=newString(response.data,HttpHeaderParser.parseCharset(response.headers));
  38. }catch(UnsupportedEncodingExceptione){
  39. parsed=newString(response.data);
  40. }
  41. returnResponse.success(parsed,HttpHeaderParser.parseCacheHeaders(response));
  42. }
  43. }

可以看到,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 plain copy
  1. publicclassXMLRequestextendsRequest<XmlPullParser>{
  2. privatefinalListener<XmlPullParser>mListener;
  3. publicXMLRequest(intmethod,Stringurl,Listener<XmlPullParser>listener,
  4. ErrorListenererrorListener){
  5. super(method,url,errorListener);
  6. mListener=listener;
  7. }
  8. publicXMLRequest(Stringurl,Listener<XmlPullParser>listener,ErrorListenererrorListener){
  9. this(Method.GET,url,listener,errorListener);
  10. }
  11. @Override
  12. protectedResponse<XmlPullParser>parseNetworkResponse(NetworkResponseresponse){
  13. try{
  14. StringxmlString=newString(response.data,
  15. HttpHeaderParser.parseCharset(response.headers));
  16. XmlPullParserFactoryfactory=XmlPullParserFactory.newInstance();
  17. XmlPullParserxmlPullParser=factory.newPullParser();
  18. xmlPullParser.setInput(newStringReader(xmlString));
  19. returnResponse.success(xmlPullParser,HttpHeaderParser.parseCacheHeaders(response));
  20. }catch(UnsupportedEncodingExceptione){
  21. returnResponse.error(newParseError(e));
  22. }catch(XmlPullParserExceptione){
  23. returnResponse.error(newParseError(e));
  24. }
  25. }
  26. @Override
  27. protectedvoiddeliverResponse(XmlPullParserresponse){
  28. mListener.onResponse(response);
  29. }
  30. }

可以看到,其实并没有什么太多的逻辑,基本都是仿照StringRequest写下来的,XMLRequest也是继承自Request类的,只不过这里指定的泛型类是XmlPullParser,说明我们准备使用Pull解析的方式来解析XML。在parseNetworkResponse()方法中,先是将服务器响应的数据解析成一个字符串,然后设置到XmlPullParser对象中,在deliverResponse()方法中则是将XmlPullParser对象进行回调。

好了,就是这么简单,下面我们尝试使用这个XMLRequest来请求一段XML格式的数据。http://flash.weather.com.cn/wmaps/xml/china.xml这个接口会将中国所有的省份数据以XML格式进行返回,如下所示:

[html] view plain copy
  1. <chinadn="day"slick-uniqueid="3">
  2. <cityquName="黑龙江"pyName="heilongjiang"cityname="哈尔滨"state1="0"state2="0"stateDetailed="晴"tem1="18"tem2="6"windState="西北风3-4级转西风小于3级"/>
  3. <cityquName="吉林"pyName="jilin"cityname="长春"state1="0"state2="0"stateDetailed="晴"tem1="19"tem2="6"windState="西北风3-4级转小于3级"/>
  4. <cityquName="辽宁"pyName="liaoning"cityname="沈阳"state1="0"state2="0"stateDetailed="晴"tem1="21"tem2="7"windState="东北风3-4级"/>
  5. <cityquName="海南"pyName="hainan"cityname="海口"state1="1"state2="1"stateDetailed="多云"tem1="30"tem2="24"windState="微风"/>
  6. <cityquName="内蒙古"pyName="neimenggu"cityname="呼和浩特"state1="0"state2="0"stateDetailed="晴"tem1="19"tem2="5"windState="东风3-4级"/>
  7. <cityquName="新疆"pyName="xinjiang"cityname="乌鲁木齐"state1="0"state2="0"stateDetailed="晴"tem1="22"tem2="10"windState="微风转东南风小于3级"/>
  8. <cityquName="西藏"pyName="xizang"cityname="拉萨"state1="1"state2="7"stateDetailed="多云转小雨"tem1="18"tem2="4"windState="微风"/>
  9. <cityquName="青海"pyName="qinghai"cityname="西宁"state1="0"state2="1"stateDetailed="晴转多云"tem1="18"tem2="2"windState="微风"/>
  10. <cityquName="宁夏"pyName="ningxia"cityname="银川"state1="0"state2="0"stateDetailed="晴"tem1="19"tem2="8"windState="微风"/>
  11. <cityquName="甘肃"pyName="gansu"cityname="兰州"state1="0"state2="0"stateDetailed="晴"tem1="21"tem2="6"windState="微风"/>
  12. <cityquName="河北"pyName="hebei"cityname="石家庄"state1="0"state2="0"stateDetailed="晴"tem1="25"tem2="12"windState="北风小于3级"/>
  13. <cityquName="河南"pyName="henan"cityname="郑州"state1="0"state2="0"stateDetailed="晴"tem1="24"tem2="13"windState="微风"/>
  14. <cityquName="湖北"pyName="hubei"cityname="武汉"state1="0"state2="0"stateDetailed="晴"tem1="24"tem2="12"windState="微风"/>
  15. <cityquName="湖南"pyName="hunan"cityname="长沙"state1="2"state2="1"stateDetailed="阴转多云"tem1="20"tem2="15"windState="北风小于3级"/>
  16. <cityquName="山东"pyName="shandong"cityname="济南"state1="1"state2="1"stateDetailed="多云"tem1="20"tem2="10"windState="北风3-4级转小于3级"/>
  17. <cityquName="江苏"pyName="jiangsu"cityname="南京"state1="2"state2="2"stateDetailed="阴"tem1="19"tem2="13"windState="西北风4-5级转3-4级"/>
  18. <cityquName="安徽"pyName="anhui"cityname="合肥"state1="2"state2="1"stateDetailed="阴转多云"tem1="20"tem2="12"windState="西北风转北风3-4级"/>
  19. <cityquName="山西"pyName="shanxi"cityname="太原"state1="0"state2="0"stateDetailed="晴"tem1="22"tem2="8"windState="微风"/>
  20. <cityquName="陕西"pyName="sanxi"cityname="西安"state1="1"state2="0"stateDetailed="多云转晴"tem1="21"tem2="9"windState="东北风小于3级"/>
  21. <cityquName="四川"pyName="sichuan"cityname="成都"state1="1"state2="1"stateDetailed="多云"tem1="26"tem2="15"windState="南风小于3级"/>
  22. <cityquName="云南"pyName="yunnan"cityname="昆明"state1="7"state2="7"stateDetailed="小雨"tem1="21"tem2="13"windState="微风"/>
  23. <cityquName="贵州"pyName="guizhou"cityname="贵阳"state1="1"state2="3"stateDetailed="多云转阵雨"tem1="21"tem2="11"windState="东风小于3级"/>
  24. <cityquName="浙江"pyName="zhejiang"cityname="杭州"state1="3"state2="1"stateDetailed="阵雨转多云"tem1="22"tem2="14"windState="微风"/>
  25. <cityquName="福建"pyName="fujian"cityname="福州"state1="1"state2="2"stateDetailed="多云转阴"tem1="28"tem2="18"windState="微风"/>
  26. <cityquName="江西"pyName="jiangxi"cityname="南昌"state1="2"state2="1"stateDetailed="阴转多云"tem1="23"tem2="15"windState="北风3-4级转微风"/>
  27. <cityquName="广东"pyName="guangdong"cityname="广州"state1="3"state2="2"stateDetailed="阵雨转阴"tem1="26"tem2="20"windState="微风"/>
  28. <cityquName="广西"pyName="guangxi"cityname="南宁"state1="3"state2="3"stateDetailed="阵雨"tem1="23"tem2="19"windState="东北风小于3级"/>
  29. <cityquName="北京"pyName="beijing"cityname="北京"state1="0"state2="0"stateDetailed="晴"tem1="26"tem2="10"windState="微风"/>
  30. <cityquName="天津"pyName="tianjin"cityname="天津"state1="1"state2="0"stateDetailed="多云转晴"tem1="22"tem2="13"windState="东北风3-4级转小于3级"/>
  31. <cityquName="上海"pyName="shanghai"cityname="上海"state1="7"state2="1"stateDetailed="小雨转多云"tem1="20"tem2="16"windState="西北风3-4级"/>
  32. <cityquName="重庆"pyName="chongqing"cityname="重庆"state1="1"state2="3"stateDetailed="多云转阵雨"tem1="21"tem2="14"windState="微风"/>
  33. <cityquName="香港"pyName="xianggang"cityname="香港"state1="3"state2="1"stateDetailed="阵雨转多云"tem1="26"tem2="22"windState="微风"/>
  34. <cityquName="澳门"pyName="aomen"cityname="澳门"state1="3"state2="1"stateDetailed="阵雨转多云"tem1="27"tem2="22"windState="东北风3-4级转微风"/>
  35. <cityquName="台湾"pyName="taiwan"cityname="台北"state1="9"state2="7"stateDetailed="大雨转小雨"tem1="28"tem2="21"windState="微风"/>
  36. <cityquName="西沙"pyName="xisha"cityname="西沙"state1="3"state2="3"stateDetailed="阵雨"tem1="30"tem2="26"windState="东北风4-5级"/>
  37. <cityquName="南沙"pyName="nanshadao"cityname="南沙"state1="1"state2="1"stateDetailed="多云"tem1="32"tem2="27"windState="东风4-5级"/>
  38. <cityquName="钓鱼岛"pyName="diaoyudao"cityname="钓鱼岛"state1="7"state2="1"stateDetailed="小雨转多云"tem1="23"tem2="19"windState="西南风3-4级转北风5-6级"/>
  39. </china>
确定了访问接口后,我们只需要在代码中按照以下的方式来使用XMLRequest即可: [java] view plain copy
  1. XMLRequestxmlRequest=newXMLRequest(
  2. "http://flash.weather.com.cn/wmaps/xml/china.xml",
  3. newResponse.Listener<XmlPullParser>(){
  4. @Override
  5. publicvoidonResponse(XmlPullParserresponse){
  6. try{
  7. inteventType=response.getEventType();
  8. while(eventType!=XmlPullParser.END_DOCUMENT){
  9. switch(eventType){
  10. caseXmlPullParser.START_TAG:
  11. StringnodeName=response.getName();
  12. if("city".equals(nodeName)){
  13. StringpName=response.getAttributeValue(0);
  14. Log.d("TAG","pNameis"+pName);
  15. }
  16. break;
  17. }
  18. eventType=response.next();
  19. }
  20. }catch(XmlPullParserExceptione){
  21. e.printStackTrace();
  22. }catch(IOExceptione){
  23. e.printStackTrace();
  24. }
  25. }
  26. },newResponse.ErrorListener(){
  27. @Override
  28. publicvoidonErrorResponse(VolleyErrorerror){
  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 plain copy
  1. publicclassGsonRequest<T>extendsRequest<T>{
  2. privatefinalListener<T>mListener;
  3. privateGsonmGson;
  4. privateClass<T>mClass;
  5. publicGsonRequest(intmethod,Stringurl,Class<T>clazz,Listener<T>listener,
  6. ErrorListenererrorListener){
  7. super(method,url,errorListener);
  8. mGson=newGson();
  9. mClass=clazz;
  10. mListener=listener;
  11. }
  12. publicGsonRequest(Stringurl,Class<T>clazz,Listener<T>listener,
  13. ErrorListenererrorListener){
  14. this(Method.GET,url,clazz,listener,errorListener);
  15. }
  16. @Override
  17. protectedResponse<T>parseNetworkResponse(NetworkResponseresponse){
  18. try{
  19. StringjsonString=newString(response.data,
  20. HttpHeaderParser.parseCharset(response.headers));
  21. returnResponse.success(mGson.fromJson(jsonString,mClass),
  22. HttpHeaderParser.parseCacheHeaders(response));
  23. }catch(UnsupportedEncodingExceptione){
  24. returnResponse.error(newParseError(e));
  25. }
  26. }
  27. @Override
  28. protectedvoiddeliverResponse(Tresponse){
  29. mListener.onResponse(response);
  30. }
  31. }
可以看到,GsonRequest是继承自Request类的,并且同样提供了两个构造函数。在parseNetworkResponse()方法中,先是将服务器响应的数据解析出来,然后通过调用Gson的fromJson方法将数据组装成对象。在deliverResponse方法中仍然是将最终的数据进行回调。

那么下面我们就来测试一下这个GsonRequest能不能够正常工作吧,调用http://www.weather.com.cn/data/sk/101010100.html这个接口可以得到一段JSON格式的天气数据,如下所示:

[plain] view plain copy
  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 plain copy
  1. publicclassWeather{
  2. privateWeatherInfoweatherinfo;
  3. publicWeatherInfogetWeatherinfo(){
  4. returnweatherinfo;
  5. }
  6. publicvoidsetWeatherinfo(WeatherInfoweatherinfo){
  7. this.weatherinfo=weatherinfo;
  8. }
  9. }
Weather类中只是引用了WeatherInfo这个类。接着新建WeatherInfo类,代码如下所示: [java] view plain copy
  1. publicclassWeatherInfo{
  2. privateStringcity;
  3. privateStringtemp;
  4. privateStringtime;
  5. publicStringgetCity(){
  6. returncity;
  7. }
  8. publicvoidsetCity(Stringcity){
  9. this.city=city;
  10. }
  11. publicStringgetTemp(){
  12. returntemp;
  13. }
  14. publicvoidsetTemp(Stringtemp){
  15. this.temp=temp;
  16. }
  17. publicStringgetTime(){
  18. returntime;
  19. }
  20. publicvoidsetTime(Stringtime){
  21. this.time=time;
  22. }
  23. }
WeatherInfo类中含有city、temp、time这几个字段。下面就是如何调用GsonRequest了,其实也很简单,代码如下所示: [java] view plain copy
  1. GsonRequest<Weather>gsonRequest=newGsonRequest<Weather>(
  2. "http://www.weather.com.cn/data/sk/101010100.html",Weather.class,
  3. newResponse.Listener<Weather>(){
  4. @Override
  5. publicvoidonResponse(Weatherweather){
  6. WeatherInfoweatherInfo=weather.getWeatherinfo();
  7. Log.d("TAG","cityis"+weatherInfo.getCity());
  8. Log.d("TAG","tempis"+weatherInfo.getTemp());
  9. Log.d("TAG","timeis"+weatherInfo.getTime());
  10. }
  11. },newResponse.ErrorListener(){
  12. @Override
  13. publicvoidonErrorResponse(VolleyErrorerror){
  14. Log.e("TAG",error.getMessage(),error);
  15. }
  16. });
  17. mQueue.add(gsonRequest);
可以看到,这里onResponse()方法的回调中直接返回了一个Weather对象,我们通过它就可以得到WeatherInfo对象,接着就能从中取出JSON中的相关数据了。现在运行一下代码,观察控制台日志,打印数据如下图所示:

这样的话,XMLRequest和GsonRequest的功能就基本都实现了,我们也是借助这两个例子深刻地理解了自定义Request的方法,对Volley的认识也是更加深入了。好了,本篇文章就到此结束,下篇文章中我们将对Volley进行更深层次的研究,感兴趣的朋友请继续阅读Android Volley完全解析(四),带你从源码的角度理解Volley

第一时间获得博客更新提醒,以及更多技术信息分享,欢迎关注我的微信公众号,扫一扫下方二维码或搜索微信号guolin_blog,即可关注。

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. 《Android开发从零开始》——25.数据存储(4)
  4. Android系统配置数据库注释(settings.db)
  5. Android中不同应用间实现SharedPreferences数据共享
  6. android图表ichartjs
  7. Android内容提供者源码
  8. android SharedPreferences
  9. Android(安卓)Paging组件Demo

随机推荐

  1. 常用 Git 命令总结
  2. Vscode安装和常用插件装置与个人对HTTP协
  3. html元素,怎么通过JS函数操作;thinkphp模板
  4. php学习
  5. 下载安装VS Code 及常用插件,与个人对Http
  6. 初次学习前端必备的开发工具VS Code简单
  7. 2021.6.28
  8. 学习环境搭建及浅谈对Http协议的理解
  9. vscode的下载和安装以及常用插件的部署和
  10. 开学第一课