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

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

这里写图片描述

这里介绍一个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的解析方式了。
至此关于自定义请求的部分已经结束。欢迎大家讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值