学习了一口一口吃掉Volley(二)之后,你应该已经学会了如何使用Volley自带的Request,但是有的时候我们需要解析的数据多种多样,例如XML又或者你想使用Google的gson,那么当Volley不能直接提供给我们这些功能的时候就需要我们进行自定义了,第一节我也向大家讲过Volley面向接口编程,使得其很容易扩展,那么这节课我们就一起来学习自定义的Request吧!
从StringRequest开始讲讲思路
先上最基本的StringRequest源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class StringRequest extends Request<String>{ private final Response.Listener<String> mListener;
public StringRequest(int method, String url, Response.Listener<String> listener, Response.ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; }
public StringRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) { this(Method.GET, url, listener, errorListener); }
@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)); }
@Override protected void deliverResponse(String response) { mListener.onResponse(response); } }
|
我们应该可以提炼出如下几点:
- StringRequest继承自Request类,并制定其泛型为String,那么当我们自定义XMLRequest时就应该指定类型为XmlPullParser。
- 有两个构造函数,默认使用的GET请求。
- 由于Request类中的deliverResponse()和parseNetworkResponse()是两个抽象方法,因此自定义Request中需要对这两个方法进行实现。
- deliverResponse()方法中仅仅是调用了mListener中的onResponse()方法,并将response内容传入即可,这样就可以将服务器响应的数据进行回调。
- parseNetworkResponse()方法中则是对服务器响应的数据进行解析,其中数据是以字节的形式存放在NetworkResponse的response变量中的,这里将数据取出然后组装成一个String,并传入Response的success()方法中即可。
既然知道内部实现的逻辑,那就开始动手吧!
自定义XMLRequest
① 直接上代码啦!
按照刚才我们解析StringRequest得到的几点结论,我们应该不难得到XMLRequest:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class XMLRequest extends Request<XmlPullParser> {
private final Response.Listener<XmlPullParser> mListener;
public XMLRequest(int method, String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; }
public XMLRequest( String url, Response.Listener<XmlPullParser> listener, Response.ErrorListener errorListener) { this(Method.GET, url, listener, errorListener); }
@Override protected Response<XmlPullParser> parseNetworkResponse(NetworkResponse response) { try { response.headers.put("HTTP.CONTENT_TYPE", "utf-8"); String xmlString = new String(response.data,"utf-8"); XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xmlPullParser = factory.newPullParser(); xmlPullParser.setInput(new StringReader(xmlString)); Log.d("SUCCESS", "xmlString的内容为" + 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); }
}
|
这里需要注意的一点我在上一篇文章中也已经提到了,就是在解析数据的时候如果出现乱码应该转换编码为utf-8。
② 测试接口
作为测试,我使用的XML接口是:http://flash.weather.com.cn/wmaps/xml/guangdong.xml ,它返回的是广东省各个城市的天气预报,你也可以将guangdong
改为你所在的省份就可以显示其他数据了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <guangdong dn="day"> <city cityX="137.7" cityY="385.95" cityname="湛江" centername="湛江" fontColor="FFFFFF" pyName="zhanjiang" state1="1" state2="1" stateDetailed="多云" tem1="23" tem2="16" temNow="24" windState="微风" windDir="东南风" windPower="2级" humidity="50%" time="15:30" url="101281001"/> <city cityX="170.65" cityY="317.55" cityname="茂名" centername="茂名" fontColor="FFFFFF" pyName="maoming" state1="1" state2="1" stateDetailed="多云" tem1="26" tem2="14" temNow="26" windState="微风" windDir="西南风" windPower="1级" humidity="47%" time="15:30" url="101282001"/> <city cityX="225" cityY="245" cityname="云浮" centername="云浮" fontColor="FFFFFF" pyName="yunfu" state1="1" state2="1" stateDetailed="多云" tem1="26" tem2="13" temNow="26" windState="微风" windDir="东风" windPower="2级" humidity="43%" time="15:30" url="101281401"/> <city cityX="226.55" cityY="304.5" cityname="阳江" centername="阳江" fontColor="FFFFFF" pyName="yangjiang" state1="0" state2="1" stateDetailed="晴转多云" tem1="24" tem2="15" temNow="23" windState="微风" windDir="东南风" windPower="3级" humidity="63%" time="15:30" url="101281801"/> <city cityX="275.35" cityY="214.65" cityname="肇庆" centername="肇庆" fontColor="FFFFFF" pyName="zhaoqing" state1="1" state2="1" stateDetailed="多云" tem1="26" tem2="14" temNow="26" windState="微风" windDir="西北风" windPower="1级" humidity="43%" time="15:30" url="101280901"/> <city cityX="291" cityY="285" cityname="江门" centername="江门" fontColor="FFFFFF" pyName="jiangmen" state1="0" state2="0" stateDetailed="晴" tem1="26" tem2="15" temNow="26" windState="微风" windDir="东风" windPower="2级" humidity="38%" time="15:30" url="101281101"/> <city cityX="313.3" cityY="160.45" cityname="清远" centername="清远" fontColor="FFFFFF" pyName="qingyuan" state1="1" state2="1" stateDetailed="多云" tem1="25" tem2="15" temNow="26" windState="微风" windDir="南风" windPower="2级" humidity="44%" time="15:30" url="101281301"/> <city cityX="308.7" cityY="225" cityname="佛山" centername="佛山" fontColor="FFFFFF" pyName="foshan" state1="0" state2="1" stateDetailed="晴转多云" tem1="26" tem2="14" temNow="26" windState="微风" windDir="北风" windPower="1级" humidity="42%" time="15:30" url="101280800"/> <city cityX="342.7" cityY="255" cityname="中山" centername="中山" fontColor="FFFFFF" pyName="zhongshan" state1="1" state2="1" stateDetailed="多云" tem1="25" tem2="14" temNow="26" windState="微风" windDir="西北风" windPower="2级" humidity="41%" time="15:30" url="101281701"/> <city cityX="340.55" cityY="300" cityname="珠海" centername="珠海" fontColor="FFFFFF" pyName="zhuhai" state1="1" state2="1" stateDetailed="多云" tem1="23" tem2="17" temNow="23" windState="微风" windDir="东风" windPower="2级" humidity="48%" time="15:30" url="101280701"/> <city cityX="352.6" cityY="80" cityname="韶关" centername="韶关" fontColor="FFFFFF" pyName="shaoguan" state1="1" state2="1" stateDetailed="多云" tem1="25" tem2="14" temNow="27" windState="微风" windDir="西风" windPower="1级" humidity="34%" time="15:30" url="101280201"/> <city cityX="353" cityY="196" cityname="广州" centername="广州" fontColor="FFFF00" pyName="guangzhou" state1="0" state2="0" stateDetailed="晴" tem1="26" tem2="14" temNow="26" windState="微风" windDir="南风" windPower="1级" humidity="40%" time="15:20" url="101280101"/> <city cityX="377" cityY="234" cityname="东莞" centername="东莞" fontColor="FFFFFF" pyName="dongguan" state1="0" state2="1" stateDetailed="晴转多云" tem1="25" tem2="15" temNow="26" windState="微风" windDir="北风" windPower="1级" humidity="37%" time="15:30" url="101281601"/> <city cityX="409" cityY="257" cityname="深圳" centername="深圳" fontColor="FFFFFF" pyName="shenzhen" state1="0" state2="0" stateDetailed="晴" tem1="24" tem2="15" temNow="24" windState="微风" windDir="南风" windPower="3级" humidity="49%" time="15:30" url="101280601"/> <city cityX="423.85" cityY="214.65" cityname="惠州" centername="惠州" fontColor="FFFFFF" pyName="huizhou" state1="1" state2="0" stateDetailed="多云转晴" tem1="26" tem2="13" temNow="26" windState="微风" windDir="南风" windPower="1级" humidity="38%" time="15:20" url="101280301"/> <city cityX="442.55" cityY="141.6" cityname="河源" centername="河源" fontColor="FFFFFF" pyName="heyuan" state1="0" state2="1" stateDetailed="晴转多云" tem1="25" tem2="15" temNow="27" windState="微风" windDir="西南风" windPower="1级" humidity="37%" time="15:30" url="101281201"/> <city cityX="492" cityY="217" cityname="汕尾" centername="汕尾" fontColor="FFFFFF" pyName="shanwei" state1="1" state2="0" stateDetailed="多云转晴" tem1="24" tem2="14" temNow="24" windState="东南风3-4级转微风" windDir="东风" windPower="2级" humidity="45%" time="15:30" url="101282101"/> <city cityX="522.55" cityY="110.45" cityname="梅州" centername="梅州" fontColor="FFFFFF" pyName="meizhou" state1="1" state2="0" stateDetailed="多云转晴" tem1="27" tem2="12" temNow="26" windState="微风" windDir="东风" windPower="1级" humidity="36%" time="15:30" url="101280401"/> <city cityX="526.8" cityY="182" cityname="揭阳" centername="揭阳" fontColor="FFFFFF" pyName="jieyang" state1="0" state2="0" stateDetailed="晴" tem1="26" tem2="13" temNow="26" windState="微风" windDir="东风" windPower="2级" humidity="34%" time="15:30" url="101281901"/> <city cityX="579" cityY="137.45" cityname="潮州" centername="潮州" fontColor="FFFFFF" pyName="chaozhou" state1="0" state2="0" stateDetailed="晴" tem1="25" tem2="12" temNow="26" windState="微风" windDir="东南风" windPower="2级" humidity="38%" time="15:30" url="101281501"/> <city cityX="566.45" cityY="179.25" cityname="汕头" centername="汕头" fontColor="FFFFFF" pyName="shantou" state1="0" state2="1" stateDetailed="晴转多云" tem1="24" tem2="13" temNow="24" windState="东北风转东风小于3级" windDir="东风" windPower="2级" humidity="54%" time="15:30" url="101280501"/> </guangdong>
|
③ 调用XMLRequest,并加入RequestQueue
代码中我将解析XML数据的城市展示在List View中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| XMLRequest xmlRequest = new XMLRequest( "http://flash.weather.com.cn/wmaps/xml/guangdong.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(2); Log.d("CITY", "PName is" + pname); citys.add(pname); } break; } eventType = response.next(); }
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(XMLRequestActivity.this, android.R.layout.simple_list_item_1, citys); lv_xml_request.setAdapter(arrayAdapter); } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { lv_xml_request.setVisibility(View.GONE); mTextView.setVisibility(View.VISIBLE); Log.d("ERROR", "返回XMLRequest失败"); } } ); mQueue.add(xmlRequest); }
|
人品爆发,看截图
XMLRequest
自定义GsonRequest
虽然Volley提供了JsonRequest为我们解析Json,但是使用JSONObject还是太麻烦了,还有很多方法可以让JSON数据解析变得更加简单,比如说GSON,所以何不如自定义一个GsonRequest呢?思路也是大同小异!
① 直接上代码啦!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class GsonRequest<T> extends Request<T> {
private final Response.Listener<T> mListener; private Gson mGson; private Class<T> mClazz;
public GsonRequest(int method, String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) { super(method, url, errorListener); mGson = new Gson(); mClazz = clazz; mListener = listener; }
public GsonRequest(String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) { this(Method.GET, url, clazz, listener, errorListener); }
@Override protected Response<T> parseNetworkResponse(NetworkResponse response) { try { response.headers.put("HTTP.CONTENT_TYPE", "utf-8"); String jsonString = new String(response.data,"utf-8"); return Response.success(mGson.fromJson(jsonString, mClazz), HttpHeaderParser.parseCacheHeaders(response) ); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } }
@Override protected void deliverResponse(T response) { mListener.onResponse(response); } }
|
同样需要注意编码的问题。在parseNetworkResponse()方法中,先是将服务器响应的数据解析出来,然后通过调用Gson的fromJson方法将数据组装成对象。在deliverResponse方法中仍然是将最终的数据进行回调。
1
| compile 'com.google.code.gson:gson:2.8.0'
|
② json接口
使用之前使用的json接口:http://www.weather.com.cn/data/sk/101010100.html ,它返回的数据是一个叫做weatherinfo的jsonObject:
1
| {"weatherinfo":{"city":"北京","cityid":"101010100","temp":"19","WD":"南风","WS":"2级","SD":"43%","WSE":"2","time":"19:45","isRadar":"1","Radar":"JC_RADAR_AZ9010_JB"}}
|
③ 新建bean类
由于返回的jsonObject名字叫做weatherinfo,所以我们新建一个WeatherInfo类,定义了几个最基本的属性就行了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 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; } }
|
另外还需要再定义一个Weather类,来获取WeatherInfo对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public class Weather { private WeatherInfo weatherinfo;
public WeatherInfo getWeatherInfo() { return weatherinfo; }
public void setWeatherInfo(WeatherInfo weatherinfo) { this.weatherinfo = weatherinfo; } } ``` >**此处的weatherinfo不可以修改为weatherInfo**
**④调用GsonRequest** 我们将获取的数据显示在Text View上就行了。 ```java 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(); if (weatherInfo != null) { StringBuffer str = new StringBuffer(); str.append("城市:" + weatherInfo.getCity() + "\n"); str.append("温度:" + weatherInfo.getTemp() + "\n"); str.append("时间:" + weatherInfo.gettime()); tv_gson_request.setText(str); Log.d("SUCCESS", "成功返回GsonRequest " + str.toString()); } else { Log.d("ERROR", "weatherinfo对象为空"); } } } , new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.d("ERROR", "返回GsonRequest失败"); tv_gson_request.setText(getResources().getString(R.string.error_message)); } } );
mQueue.add(gsonRequest);
|
看截图啦!
GsonRequest
这一节只是向大家介绍了如何自定义Request,其实都是大同小异的,例如你觉得Gson不能满足你,那你也可以改为FastJson或者其他的功能,但是请一定要注意编码问题!最后一节我们将一起通过阅读源码来分析Volley的工作流程!
v1.5.2