Android Volley完全解析(三),定制自己的Request

1. 自定义XMLRequest

下面我们准备自定义一个XMLRequest,用于请求一条XML格式的数据。那么该从哪里开始入手呢?额,好像是有些无从下手。遇到这种情况,我们应该去参考一下Volley的源码,看一看StringRequest是怎么实现的,然后就可以模仿着写出XMLRequest了。首先看下StringRequest的源码,如下所示:

/** 
 * 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));  
        } catch (UnsupportedEncodingException e) {  
            parsed = new String(response.data);  
        }  
        return Response.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了,代码如下所示:

public class XMLRequest extends Request<XmlPullParser> {  
  
    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));  
            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);  
    }  
  
}  

可以看到,其实并没有什么太多的逻辑,基本都是仿照StringRequest写下来的,XMLRequest也是继承自Request类的,只不过这里指定的泛型类是XmlPullParser,说明我们准备使用Pull解析的方式来解析XML。在parseNetworkResponse()方法中,先是将服务器响应的数据解析成一个字符串,然后设置到XmlPullParser对象中,在deliverResponse()方法中则是将XmlPullParser对象进行回调。

好了,就是这么简单,下面我们尝试使用这个XMLRequest来请求一段XML格式的数据。http://flash.weather.com.cn/wmaps/xml/china.xml这个接口会将中国所有的省份数据以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();  
                }  
            }  
        }, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                Log.e("TAG", error.getMessage(), error);  
            }  
        });  
mQueue.add(xmlRequest);  

可以看到,这里XMLRequest的用法和StringRequest几乎是一模一样的,我们先创建出一个XMLRequest的实例,并把服务器接口地址传入,然后在onResponse()方法中解析响应的XML数据,并把每个省的名字打印出来,最后将这个XMLRequest添加到RequestQueue当中。

现在运行一下代码,观察控制台日志,就可以看到每个省的名字都从XML中解析出来了,如下图所示。

Android Volley完全解析(三),定制自己的Request

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,代码如下所示:

public class GsonRequest<T> extends Request<T> {  
  
    private final Listener<T> mListener;  
  
    private Gson mGson;  
  
    private Class<T> mClass;  
  
    public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener,  
            ErrorListener errorListener) {  
        super(method, url, errorListener);  
        mGson = new Gson();  
        mClass = clazz;  
        mListener = listener;  
    }  
  
    public GsonRequest(String url, Class<T> clazz, Listener<T> listener,  
            ErrorListener errorListener) {  
        this(Method.GET, url, clazz, listener, errorListener);  
    }  
  
    @Override  
    protected Response<T> parseNetworkResponse(NetworkResponse response) {  
        try {  
            String jsonString = new String(response.data,  
                    HttpHeaderParser.parseCharset(response.headers));  
            return Response.success(mGson.fromJson(jsonString, mClass),  
                    HttpHeaderParser.parseCacheHeaders(response));  
        } catch (UnsupportedEncodingException e) {  
            return Response.error(new ParseError(e));  
        }  
    }  
  
    @Override  
    protected void deliverResponse(T response) {  
        mListener.onResponse(response);  
    }  
  
}  

可以看到,GsonRequest是继承自Request类的,并且同样提供了两个构造函数。在parseNetworkResponse()方法中,先是将服务器响应的数据解析出来,然后通过调用Gson的fromJson方法将数据组装成对象。在deliverResponse方法中仍然是将最终的数据进行回调。

那么下面我们就来测试一下这个GsonRequest能不能够正常工作吧,调用http://www.weather.com.cn/data/sk/101010100.html这个接口可以得到一段JSON格式的天气数据,如下所示:

public class Weather {  
  
    private WeatherInfo weatherinfo;  
  
    public WeatherInfo getWeatherinfo() {  
        return weatherinfo;  
    }  
  
    public void setWeatherinfo(WeatherInfo weatherinfo) {  
        this.weatherinfo = weatherinfo;  
    }  
  
}  

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

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;  
    }  
  
}  

WeatherInfo类中含有city、temp、time这几个字段。下面就是如何调用GsonRequest了,其实也很简单,代码如下所示:

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();  
                Log.d("TAG", "city is " + weatherInfo.getCity());  
                Log.d("TAG", "temp is " + weatherInfo.getTemp());  
                Log.d("TAG", "time is " + weatherInfo.getTime());  
            }  
        }, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                Log.e("TAG", error.getMessage(), error);  
            }  
        });  
mQueue.add(gsonRequest);  

可以看到,这里onResponse()方法的回调中直接返回了一个Weather对象,我们通过它就可以得到WeatherInfo对象,接着就能从中取出JSON中的相关数据了。现在运行一下代码,观察控制台日志,打印数据如下图所示:

Android Volley完全解析(三),定制自己的Request

这样的话,XMLRequest和GsonRequest的功能就基本都实现了,我们也是借助这两个例子深刻地理解了自定义Request的方法,对Volley的认识也是更加深入了。

相关推荐