转载请注明出处: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

  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 plaincopy

  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 plaincopy

  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. </china>

确定了访问接口后,我们只需要在代码中按照以下的方式来使用XMLRequest即可:

[java]view plaincopy

  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 plaincopy

  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 plaincopy

  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

  1. publicclassWeather{

  2. privateWeatherInfoweatherinfo;

  3. publicWeatherInfogetWeatherinfo(){

  4. returnweatherinfo;

  5. }

  6. publicvoidsetWeatherinfo(WeatherInfoweatherinfo){

  7. this.weatherinfo=weatherinfo;

  8. }

  9. }

Weather类中只是引用了WeatherInfo这个类。接着新建WeatherInfo类,代码如下所示:

[java]view plaincopy

  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 plaincopy

  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的认识也是更加深入了。

更多相关文章

  1. Android(安卓)Lock 随笔
  2. Android短彩信收发流程(Framework)
  3. Android(安卓)Activity和Intent机制学习笔记
  4. Android(安卓)media媒体库分析之:分类别统计媒体文件大小
  5. delphi xe5 android 开发数据访问server端(一)
  6. [Android中react-native调用Native]
  7. selector和自定义显示状态
  8. Android(安卓)Retrofit 源码系列(三)~ 整合 RxJava、Coroutine 分
  9. Android模拟器增大内存的方法

随机推荐

  1. Android 中 android.view.WindowLeaked的
  2. android 从相机或者相册获取照片!
  3. Android所有可用的资源列表
  4. Android(安卓)调用相机,获取的图片太小
  5. Android Studio 新建drawable-hdpi、draw
  6. 如何在Android当中显示网络图片
  7. Android 实现导航菜单左右滑动效果
  8. Imageview.setAdjustViewBounds用法
  9. android用shape画虚线
  10. Android clipToPadding和clipChildren属