Volley网络框架学习笔记(三)

时间:2022-09-15 12:32:40

通过前面两篇学习,我们基本明白了Volley网络框架通过stringRequest和JsonArrayRequest来获取字符串和json串,也学习了用三种方式来加载图片。需要的同学可以去看看前两篇笔记:
Volley网络框架学习笔记(一)
http://blog.csdn.net/linshijun33/article/details/47954693
Volley网络框架学习笔记(一)
http://blog.csdn.net/linshijun33/article/details/47989141

然而我们知道,Volley的强大之处不仅如此。Volley的所有的请求的超类型是Resuest,常用的请求都是这个类的子类,自定义request肯定也是基于这个类的。

Volley提供了非常强的扩展机制,使得我们可以很轻松地定制出任意类型的Request。

首先我们去查看一下源码,然后就可以仿造源码的方式来自定义我们的Request.直接来看StringRequestr的源码

** 
* 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 {@link 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));
//HttpHeaderParser.parseCharset(response.headers)其实就是获取我们的返回流的编码,如服务器设置的utf-8
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));

}
}

这里分析一下源码,StringRequest是继承是Request的,Request是一个泛型,这里指定为String类型的。接下来StringRequest中提供了两个有参的构造函数,参数包括请求类型,请求地址,以及响应回调等。在构造函数中一定要调用super()方法将这几个参数传给父类,因为HTTP的请求和响应都是在父类中自动处理的。

由于Request类中的deliverResponse()和parseNetworkResponse()是两个抽象方法,因此StringRequest中需要对这两个方法进行实现。deliverResponse()方法中的实现很简单,仅仅是调用了mListener中的onResponse()方法,并将response内容传入即可,这样就可以将服务器响应的数据进行回调了。parseNetworkResponse()方法中则应该对服务器响应的数据进行解析,其中数据是以字节的形式存放在NetworkResponse的data变量中的,这里将数据取出然后组装成一个String,并传入Response的success()方法中即可。

我们都知道,除了json外,我们还可以xml格式的数据。在这里可以实现一个XMLRequest

public class XMLRequest extends Request<XmlPullParser> {  
// XmlPullParser类型的Listener
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));
//Xml解析的过程
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);
}

}

值得注意的是:在parseNetworkResponse()方法中,先是将服务器响应的数据解析成一个字符串,然后设置到XmlPullParser对象中,在deliverResponse()方法中则是将XmlPullParser对象进行回调。

可以练习一下解析Yahoo的XML格式的天气预报,获取当天和最近几天的天气:
http://weather.yahooapis.com/forecastrss?u=c&w=2158433
参数w是城市代码,要查询某个城市代码,可以在weather.yahoo.com搜索城市,浏览器地址栏的URL就包含城市代码

/**
* 用自定义的XMLRequest来下载雅虎天气返回的XML数据
*/


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();
}
}
// 下面这个地方要注意在之前导入的errorListener的包一定要是volley response
// 不能是xml的error response
}, new Response.ErrorListener() {

@Override
public void onErrorResponse(VolleyError volleyError) {
Log.e("TAG", volleyError.getMessage(), volleyError);
}
});
mQueue.add(xmlRequest);

Volley网络框架学习笔记(三)

这里介绍一个xml数据是如何解析的。
能够运用在Android系统上解析XML文件的常用有三种方式:DOM、SAX和PULL,其中DOM解析XML是先把XML文件读进内存中,再通过接口获取数据,该方法使用相对小的XML文件,移动设备往往受硬件性能影响,如果XML文件比较大使用DOM解析往往效率跟不上;SAX和PULL都是采用事件驱动方式来进行解析,在Android中的事件机制是基于回调函数。
本例旨在考虑简单方便性,综合考虑选择了PULL解析,PULL解析器是一个开源项目,Android平台已经内置了PULL解析器,同时Android系统本身也是使用PULL解析器来解析各种XML文档。
PULL解析XML文件时,回调XmlResourceParser内定义表示文档开头结束和节点开头结束的数值(事件回调类型),表示如下:
事件回调类型:
a.读取到XML文档开头(声明)返回:XmlPullParser.START_DOCUMENT(0)
b.读取到XML文档结束返回:XmlPullParser.END_DOCUMENT (1)
c.读取到XML节点开始返回:XmlPullParser.START_TAG (2)
d.读取到XML节点结束返回:XmlPullParser.END_TAG (3)
e.读取到XML文本返回:XmlPullParser.TEXT (4)

分别代表着XML文档的结束,开始,标签的开始,标签的结束,内容
按照以上的格式依次进行解析即可。
XmlPullParser有几个主要方法(更多查阅Android APIs):
a.XmlPullParser.getEventType() : Returns the type of the current event (START_TAG, END_TAG, TEXT, etc.) 【获取当前事件回调类型】
b.XmlPullParser.getName():For START_TAG or END_TAG events, the (local) name of the current element is returned when namespaces are enabled.【获取当前节点名字】
c.XmlPullParser.getAttributeValue(int index):Returns the given attributes value.【根据id获取节点属性值】
d.XmlPullParser.getAttributeValue(String namespace, String name):Returns the attributes value identified by namespace URI and namespace localName.【根据name获取节点属性值】
e.XmlPullParser.netxText():If current event is START_TAG then if next element is TEXT then element content is returned or if next event is END_TAG then empty string is returned, otherwise exception is thrown.【回调节点START_TAG时,通过此方法获取节点内容】
具体的实例如下:

try {  
//开始解析事件
int eventType = parser.getEventType();

//处理事件,不碰到文档结束就一直处理
while (eventType != XmlPullParser.END_DOCUMENT) {
//因为定义了一堆静态常量,所以这里可以用switch
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
// 不做任何操作或初开始化数据
break;

case XmlPullParser.START_TAG:
// 解析XML节点数据
// 获取当前标签名字
String tagName = parser.getName();

if(tagName.equals("XXXTAGXXX")){

// 通过getAttributeValue 和 netxText解析节点的属性值和节点值

}
break;

case XmlPullParser.END_TAG:
// 单节点完成,可往集合里边添加新的数据
break;
case XmlPullParser.END_DOCUMENT:

break;
}

// 别忘了用next方法处理下一个事件,不然就会死循环
eventType = parser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}

光这么说大家可能还是不理解,所以还是来上实例吧
首先展示一个我们要用的XML,从本地载入的方式就不说了。

xml version="1.0" encoding="UTF-8"?>  
<books>
<book id="12">
<name>thinking in javaname>
<price>85.5price>
book>
<book id="15">
<name>Spring in Actionname>
<price>39.0price>
book>
books>
       //先定义对应对象,books及book
List books = null;
Book book = null;
// 实例化pullparser
XmlPullParser parser = Xml.newPullParser();
// 设置编码,防止乱码
parser.setInput(inputStream, "UTF-8");

int event = parser.getEventType();//产生第一个事件
while(event!=XmlPullParser.END_DOCUMENT){
switch(event){
case XmlPullParser.START_DOCUMENT://判断当前事件是否是文档开始事件
books = new ArrayList();//初始化books集合
break;
case XmlPullParser.START_TAG://判断当前事件是否是标签元素开始事件
if("book".equals(parser.getName())){//判断开始标签元素是否是book
book = new Book();
//book的set方法,在pojo中的getter、setter方法,我的博客关于MVP模式中有讲解,大家应该不陌生吧 book.setId(Integer.parseInt(parser.getAttributeValue(0)));//得到book标签的属性值,并设置book的id
}
if(book!=null){
if("name".equals(parser.getName())){//判断开始标签元素是否是name
book.setName(parser.nextText());
}else if("price".equals(parser.getName())){//判断开始标签元素是否是price
book.setPrice(Float.parseFloat(parser.nextText()));
}
}
break;
case XmlPullParser.END_TAG://判断当前事件是否是标签元素结束事件
if("book".equals(parser.getName())){//判断结束标签元素是否是book
books.add(book);//将book添加到books集合
book = null;
}
break;
}
event = parser.next();//进入下一个元素并触发相应事件
}//end while
return books;
}

通过这个例子就比较能理解xml的解析方式了。
至此关于自定义请求的部分已经结束。欢迎大家讨论。