(4.2.10.2)【android开源工具】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 plain copy- /**
- *AcannedrequestforretrievingtheresponsebodyatagivenURLasaString.
- */
- publicclassStringRequestextendsRequest<String>{
- privatefinalListener<String>mListener;
- /**
- *Createsanewrequestwiththegivenmethod.
- *
- *@parammethodtherequest{@linkMethod}touse
- *@paramurlURLtofetchthestringat
- *@paramlistenerListenertoreceivetheStringresponse
- *@paramerrorListenerErrorlistener,ornulltoignoreerrors
- */
- publicStringRequest(intmethod,Stringurl,Listener<String>listener,
- ErrorListenererrorListener){
- super(method,url,errorListener);
- mListener=listener;
- }
- /**
- *CreatesanewGETrequest.
- *
- *@paramurlURLtofetchthestringat
- *@paramlistenerListenertoreceivetheStringresponse
- *@paramerrorListenerErrorlistener,ornulltoignoreerrors
- */
- publicStringRequest(Stringurl,Listener<String>listener,ErrorListenererrorListener){
- this(Method.GET,url,listener,errorListener);
- }
- @Override
- protectedvoiddeliverResponse(Stringresponse){
- mListener.onResponse(response);
- }
- @Override
- protectedResponse<String>parseNetworkResponse(NetworkResponseresponse){
- Stringparsed;
- try{
- parsed=newString(response.data,HttpHeaderParser.parseCharset(response.headers));
- }catch(UnsupportedEncodingExceptione){
- parsed=newString(response.data);
- }
- returnResponse.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了,代码如下所示:
[java] view plain copy- publicclassXMLRequestextendsRequest<XmlPullParser>{
- privatefinalListener<XmlPullParser>mListener;
- publicXMLRequest(intmethod,Stringurl,Listener<XmlPullParser>listener,
- ErrorListenererrorListener){
- super(method,url,errorListener);
- mListener=listener;
- }
- publicXMLRequest(Stringurl,Listener<XmlPullParser>listener,ErrorListenererrorListener){
- this(Method.GET,url,listener,errorListener);
- }
- @Override
- protectedResponse<XmlPullParser>parseNetworkResponse(NetworkResponseresponse){
- try{
- StringxmlString=newString(response.data,
- HttpHeaderParser.parseCharset(response.headers));
- XmlPullParserFactoryfactory=XmlPullParserFactory.newInstance();
- XmlPullParserxmlPullParser=factory.newPullParser();
- xmlPullParser.setInput(newStringReader(xmlString));
- returnResponse.success(xmlPullParser,HttpHeaderParser.parseCacheHeaders(response));
- }catch(UnsupportedEncodingExceptione){
- returnResponse.error(newParseError(e));
- }catch(XmlPullParserExceptione){
- returnResponse.error(newParseError(e));
- }
- }
- @Override
- protectedvoiddeliverResponse(XmlPullParserresponse){
- 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格式进行返回,如下所示:
[html] view plain copy- <chinadn="day"slick-uniqueid="3">
- <cityquName="黑龙江"pyName="heilongjiang"cityname="哈尔滨"state1="0"state2="0"stateDetailed="晴"tem1="18"tem2="6"windState="西北风3-4级转西风小于3级"/>
- <cityquName="吉林"pyName="jilin"cityname="长春"state1="0"state2="0"stateDetailed="晴"tem1="19"tem2="6"windState="西北风3-4级转小于3级"/>
- <cityquName="辽宁"pyName="liaoning"cityname="沈阳"state1="0"state2="0"stateDetailed="晴"tem1="21"tem2="7"windState="东北风3-4级"/>
- <cityquName="海南"pyName="hainan"cityname="海口"state1="1"state2="1"stateDetailed="多云"tem1="30"tem2="24"windState="微风"/>
- <cityquName="内蒙古"pyName="neimenggu"cityname="呼和浩特"state1="0"state2="0"stateDetailed="晴"tem1="19"tem2="5"windState="东风3-4级"/>
- <cityquName="新疆"pyName="xinjiang"cityname="乌鲁木齐"state1="0"state2="0"stateDetailed="晴"tem1="22"tem2="10"windState="微风转东南风小于3级"/>
- <cityquName="西藏"pyName="xizang"cityname="拉萨"state1="1"state2="7"stateDetailed="多云转小雨"tem1="18"tem2="4"windState="微风"/>
- <cityquName="青海"pyName="qinghai"cityname="西宁"state1="0"state2="1"stateDetailed="晴转多云"tem1="18"tem2="2"windState="微风"/>
- <cityquName="宁夏"pyName="ningxia"cityname="银川"state1="0"state2="0"stateDetailed="晴"tem1="19"tem2="8"windState="微风"/>
- <cityquName="甘肃"pyName="gansu"cityname="兰州"state1="0"state2="0"stateDetailed="晴"tem1="21"tem2="6"windState="微风"/>
- <cityquName="河北"pyName="hebei"cityname="石家庄"state1="0"state2="0"stateDetailed="晴"tem1="25"tem2="12"windState="北风小于3级"/>
- <cityquName="河南"pyName="henan"cityname="郑州"state1="0"state2="0"stateDetailed="晴"tem1="24"tem2="13"windState="微风"/>
- <cityquName="湖北"pyName="hubei"cityname="武汉"state1="0"state2="0"stateDetailed="晴"tem1="24"tem2="12"windState="微风"/>
- <cityquName="湖南"pyName="hunan"cityname="长沙"state1="2"state2="1"stateDetailed="阴转多云"tem1="20"tem2="15"windState="北风小于3级"/>
- <cityquName="山东"pyName="shandong"cityname="济南"state1="1"state2="1"stateDetailed="多云"tem1="20"tem2="10"windState="北风3-4级转小于3级"/>
- <cityquName="江苏"pyName="jiangsu"cityname="南京"state1="2"state2="2"stateDetailed="阴"tem1="19"tem2="13"windState="西北风4-5级转3-4级"/>
- <cityquName="安徽"pyName="anhui"cityname="合肥"state1="2"state2="1"stateDetailed="阴转多云"tem1="20"tem2="12"windState="西北风转北风3-4级"/>
- <cityquName="山西"pyName="shanxi"cityname="太原"state1="0"state2="0"stateDetailed="晴"tem1="22"tem2="8"windState="微风"/>
- <cityquName="陕西"pyName="sanxi"cityname="西安"state1="1"state2="0"stateDetailed="多云转晴"tem1="21"tem2="9"windState="东北风小于3级"/>
- <cityquName="四川"pyName="sichuan"cityname="成都"state1="1"state2="1"stateDetailed="多云"tem1="26"tem2="15"windState="南风小于3级"/>
- <cityquName="云南"pyName="yunnan"cityname="昆明"state1="7"state2="7"stateDetailed="小雨"tem1="21"tem2="13"windState="微风"/>
- <cityquName="贵州"pyName="guizhou"cityname="贵阳"state1="1"state2="3"stateDetailed="多云转阵雨"tem1="21"tem2="11"windState="东风小于3级"/>
- <cityquName="浙江"pyName="zhejiang"cityname="杭州"state1="3"state2="1"stateDetailed="阵雨转多云"tem1="22"tem2="14"windState="微风"/>
- <cityquName="福建"pyName="fujian"cityname="福州"state1="1"state2="2"stateDetailed="多云转阴"tem1="28"tem2="18"windState="微风"/>
- <cityquName="江西"pyName="jiangxi"cityname="南昌"state1="2"state2="1"stateDetailed="阴转多云"tem1="23"tem2="15"windState="北风3-4级转微风"/>
- <cityquName="广东"pyName="guangdong"cityname="广州"state1="3"state2="2"stateDetailed="阵雨转阴"tem1="26"tem2="20"windState="微风"/>
- <cityquName="广西"pyName="guangxi"cityname="南宁"state1="3"state2="3"stateDetailed="阵雨"tem1="23"tem2="19"windState="东北风小于3级"/>
- <cityquName="北京"pyName="beijing"cityname="北京"state1="0"state2="0"stateDetailed="晴"tem1="26"tem2="10"windState="微风"/>
- <cityquName="天津"pyName="tianjin"cityname="天津"state1="1"state2="0"stateDetailed="多云转晴"tem1="22"tem2="13"windState="东北风3-4级转小于3级"/>
- <cityquName="上海"pyName="shanghai"cityname="上海"state1="7"state2="1"stateDetailed="小雨转多云"tem1="20"tem2="16"windState="西北风3-4级"/>
- <cityquName="重庆"pyName="chongqing"cityname="重庆"state1="1"state2="3"stateDetailed="多云转阵雨"tem1="21"tem2="14"windState="微风"/>
- <cityquName="香港"pyName="xianggang"cityname="香港"state1="3"state2="1"stateDetailed="阵雨转多云"tem1="26"tem2="22"windState="微风"/>
- <cityquName="澳门"pyName="aomen"cityname="澳门"state1="3"state2="1"stateDetailed="阵雨转多云"tem1="27"tem2="22"windState="东北风3-4级转微风"/>
- <cityquName="台湾"pyName="taiwan"cityname="台北"state1="9"state2="7"stateDetailed="大雨转小雨"tem1="28"tem2="21"windState="微风"/>
- <cityquName="西沙"pyName="xisha"cityname="西沙"state1="3"state2="3"stateDetailed="阵雨"tem1="30"tem2="26"windState="东北风4-5级"/>
- <cityquName="南沙"pyName="nanshadao"cityname="南沙"state1="1"state2="1"stateDetailed="多云"tem1="32"tem2="27"windState="东风4-5级"/>
- <cityquName="钓鱼岛"pyName="diaoyudao"cityname="钓鱼岛"state1="7"state2="1"stateDetailed="小雨转多云"tem1="23"tem2="19"windState="西南风3-4级转北风5-6级"/>
- </china>
- XMLRequestxmlRequest=newXMLRequest(
- "http://flash.weather.com.cn/wmaps/xml/china.xml",
- newResponse.Listener<XmlPullParser>(){
- @Override
- publicvoidonResponse(XmlPullParserresponse){
- try{
- inteventType=response.getEventType();
- while(eventType!=XmlPullParser.END_DOCUMENT){
- switch(eventType){
- caseXmlPullParser.START_TAG:
- StringnodeName=response.getName();
- if("city".equals(nodeName)){
- StringpName=response.getAttributeValue(0);
- Log.d("TAG","pNameis"+pName);
- }
- break;
- }
- eventType=response.next();
- }
- }catch(XmlPullParserExceptione){
- e.printStackTrace();
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- },newResponse.ErrorListener(){
- @Override
- publicvoidonErrorResponse(VolleyErrorerror){
- Log.e("TAG",error.getMessage(),error);
- }
- });
- mQueue.add(xmlRequest);
现在运行一下代码,观察控制台日志,就可以看到每个省的名字都从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- publicclassGsonRequest<T>extendsRequest<T>{
- privatefinalListener<T>mListener;
- privateGsonmGson;
- privateClass<T>mClass;
- publicGsonRequest(intmethod,Stringurl,Class<T>clazz,Listener<T>listener,
- ErrorListenererrorListener){
- super(method,url,errorListener);
- mGson=newGson();
- mClass=clazz;
- mListener=listener;
- }
- publicGsonRequest(Stringurl,Class<T>clazz,Listener<T>listener,
- ErrorListenererrorListener){
- this(Method.GET,url,clazz,listener,errorListener);
- }
- @Override
- protectedResponse<T>parseNetworkResponse(NetworkResponseresponse){
- try{
- StringjsonString=newString(response.data,
- HttpHeaderParser.parseCharset(response.headers));
- returnResponse.success(mGson.fromJson(jsonString,mClass),
- HttpHeaderParser.parseCacheHeaders(response));
- }catch(UnsupportedEncodingExceptione){
- returnResponse.error(newParseError(e));
- }
- }
- @Override
- protectedvoiddeliverResponse(Tresponse){
- mListener.onResponse(response);
- }
- }
那么下面我们就来测试一下这个GsonRequest能不能够正常工作吧,调用http://www.weather.com.cn/data/sk/101010100.html这个接口可以得到一段JSON格式的天气数据,如下所示:
[plain] view plain copy- {"weatherinfo":{"city":"北京","cityid":"101010100","temp":"19","WD":"南风","WS":"2级","SD":"43%","WSE":"2","time":"19:45","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB"}}
- publicclassWeather{
- privateWeatherInfoweatherinfo;
- publicWeatherInfogetWeatherinfo(){
- returnweatherinfo;
- }
- publicvoidsetWeatherinfo(WeatherInfoweatherinfo){
- this.weatherinfo=weatherinfo;
- }
- }
- publicclassWeatherInfo{
- privateStringcity;
- privateStringtemp;
- privateStringtime;
- publicStringgetCity(){
- returncity;
- }
- publicvoidsetCity(Stringcity){
- this.city=city;
- }
- publicStringgetTemp(){
- returntemp;
- }
- publicvoidsetTemp(Stringtemp){
- this.temp=temp;
- }
- publicStringgetTime(){
- returntime;
- }
- publicvoidsetTime(Stringtime){
- this.time=time;
- }
- }
- GsonRequest<Weather>gsonRequest=newGsonRequest<Weather>(
- "http://www.weather.com.cn/data/sk/101010100.html",Weather.class,
- newResponse.Listener<Weather>(){
- @Override
- publicvoidonResponse(Weatherweather){
- WeatherInfoweatherInfo=weather.getWeatherinfo();
- Log.d("TAG","cityis"+weatherInfo.getCity());
- Log.d("TAG","tempis"+weatherInfo.getTemp());
- Log.d("TAG","timeis"+weatherInfo.getTime());
- }
- },newResponse.ErrorListener(){
- @Override
- publicvoidonErrorResponse(VolleyErrorerror){
- Log.e("TAG",error.getMessage(),error);
- }
- });
- mQueue.add(gsonRequest);
这样的话,XMLRequest和GsonRequest的功能就基本都实现了,我们也是借助这两个例子深刻地理解了自定义Request的方法,对Volley的认识也是更加深入了。好了,本篇文章就到此结束,下篇文章中我们将对Volley进行更深层次的研究,感兴趣的朋友请继续阅读Android Volley完全解析(四),带你从源码的角度理解Volley。
第一时间获得博客更新提醒,以及更多技术信息分享,欢迎关注我的微信公众号,扫一扫下方二维码或搜索微信号guolin_blog,即可关注。
更多相关文章
- mybatisplus的坑 insert标签insert into select无参数问题的解决
- python起点网月票榜字体反爬案例
- 《Android开发从零开始》——25.数据存储(4)
- Android系统配置数据库注释(settings.db)
- Android中不同应用间实现SharedPreferences数据共享
- android图表ichartjs
- Android内容提供者源码
- android SharedPreferences
- Android(安卓)Paging组件Demo