Volley源码解析(三)

来源:互联网 发布:java 图形验证码 开源 编辑:程序博客网 时间:2024/05/20 18:51

在《Volley源码解析(一)》和《Volley源码解析(二)》中简单讲解了Volley的内部工作原理,一言以蔽之就是:创建请求Request对象,然后将请求对象放入队列RequestQueue中,开启线程NetworkDispatcher不断从线程中获取请求对象,然后执行请求并返回响应对象。具体的细节可参考上面两篇博文,在此不细说,关于Volley的使用可以参考其官方文档。

其实对于android来说,大部分网络请求的业务都是通过接口获取数据然后对UI进行渲染;对于Volley来说你可以在任意的线程(UI线程或者非UI线程)创建Request对象,但是通过NetworkDispatcher发起网络请求然后完成数据解析后,最终解析后的数据会传给UI线程(Main Thread)来使用。那么这个过程是怎么实现的呢?在NetWorkDispatcher这个thread的run方法的内部有如下一句代码(详见《Volley源码解析(二)》):

 //主要是回调Listener接口,通知客户端获取数据 mDelivery.postResponse(request, response);

事实上这段代码就是将解析后的response对象交给客户端使用,确切的说是在将数据交给main thread使用,这个mDelivery是ResponseDelivery接口,其具体在Volley的具体体现是ExecutorDelivery。那么mDelivery是什么时候初始化的呢?

在初始化请求队列RequestQueue对象的时候会通过Volley.newRequestQueue(context) 几经辗转会调用:

 public RequestQueue(Cache cache, Network network, int threadPoolSize) {        this(cache, network, threadPoolSize,                new ExecutorDelivery(new Handler(Looper.getMainLooper())));    }

可以看出mDelivery变量的具体实例就是ExecutorDelivery,且该对象包含一个Handler对象,到此为止Volley的工作总体思路就不难猜出了:
1、异步线程发起网络请求
2、将请求包换的数据通过handler发送给main thread
这样就Volley就保证了获取网络数据回调更新UI界面过程,关于handler的工作机制可以参考博主的《android消息处理机制详解》

下面就看看ExecutorDelivery的post方法都作了写什么?

 private final Executor mResponsePoster;    public ExecutorDelivery(final Handler handler) {        // Make an Executor that just wraps the handler.        //该Executor只是将Runable经过hanler重新包装一下,        mResponsePoster = new Executor() {            @Override            public void execute(Runnable command) {                handler.post(command);            }        };    }

从上面代码来看ExecutorDelivery的作用就是将交给Executor对象的Runnable交给handler处理,所以现在就可以看看post方法都作了写什么:

 @Override    public void postResponse(Request<?> request, Response<?> response) {        postResponse(request, response, null);    }    @Override    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));    }

上面代码很简单,就是将ResponseDeliveryRunnable交给mResponsePoster这个Executor,最终交给handler处理。所以我们来看看ResponseDeliveryRunnable都作了写什么:

 public void run() {           //省略部分代码            if (mResponse.isSuccess()) {                mRequest.deliverResponse(mResponse.result);            } else {                mRequest.deliverError(mResponse.error);            }            //省略部分代码       }

上面的逻辑也很简单,就是将回调相关接口来通知客户端网络返回结果,如果失败的话回调ErrorListener接口,将错误原因交给客户端,成功则交给Request对象的子类来处理(因为deliverResponse是一个抽象方法)。以StringRequest对象来举例,其deliverResponse方法实现如下:

  @Override    protected void deliverResponse(String response) {        if (mListener != null) {            mListener.onResponse(response);        }    }

所以如果如果以StringRequest请求对象对正确和错误的响应都感兴趣的话,可以如下构建StringRequest对象:

  RequestQueue queue = Volley.newRequestQueue(this);        String url = "http://www.baidu.com";        //请求成功的接口        Response.Listener<String> requestSuccessListener = new Response.Listener<String>() {            @Override            public void onResponse(String response) {                Log.i("VolleryTest","请求成功"+response);            }        };       //请求失败的接口        Response.ErrorListener errorListener = new Response.ErrorListener() {            @Override            public void onErrorResponse(VolleyError error) {                Log.i("VolleryTest","请求失败");                error.printStackTrace();            }        };  //构建请求对象:此处为get请求       StringRequest request = new StringRequest( url,requestSuccessListener,errorListener); queue.add(request);

运行上述代码发现打印请求失败,也就是说走了ErrorListener,并且Volley丢给我一个内部的log:

Unexpected response code 302

奇哉怪也,使用okhttp请求百度首页并没有出现这个错误,于是追踪其源码,在BasicNetWork这个类中找到类原因(这个类具体干嘛的,参考博主的其他两篇volley博客,在此不细说):

  if (statusCode < 200 || statusCode > 299) {        throw new IOException();   }

一目了然,Volley对于http的状态码作了判断,不满足200 =

 VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());

所以从对状态码的处理来看,OKhttp的处理比Volley理智多了,并没有一棒子打死。

Volley对http方法的支持

我们知道http支持get,post,put,delete等方法,那么volley又是支持哪些http的方法呢?查看Request代码可以发现volley对常用的http方法都作了支持:

 /**     * Supported request methods.     */    public interface Method {        int DEPRECATED_GET_OR_POST = -1;        int GET = 0;        int POST = 1;        int PUT = 2;        int DELETE = 3;        int HEAD = 4;        int OPTIONS = 5;        int TRACE = 6;        int PATCH = 7;    }

get请求不用说,下面来简单分析下post请求是怎么实现的,确切的收是分析post请求是怎么携带参数的。在HurlStack类中提供了一个setConnectionParametersForRequest方法:

static void setConnectionParametersForRequest(HttpURLConnection connection,            Request<?> request) {        switch (request.getMethod()) {            case Method.POST:                connection.setRequestMethod("POST");                addBodyIfExists(connection, request);                break;        }    }

如果是post请求,则会调用addBodyIfExists方法来组建参数(如果有参数的话):

 static void addBodyIfExists(HttpURLConnection connection, Request<?> request) {        byte[] body = request.getBody();        //省略组建post参数的代码    }

addBodyIfExists的第一步是通过Request对象的getBody方法来获取post请求所需的参数,所以进度该方法看看是怎么回事儿:

  public byte[] getBody(){        //通过getParams方法获取post请求参数        Map<String, String> params = getParams();        if (params != null && params.size() > 0) {            return encodeParameters(params, getParamsEncoding());        }        return null;    }

简单明了,原来post,delete,等请求参数的构建是通过Request对象的getParams() 方法来实现的:

 protected Map<String, String> getParams() {        return null;    }

该方法是一个protected方法,所以如果以StringReqeust 对象发起post请求的话,那么代码就该是如下所示 了:

new StringRequest(Request.Method.POST,url,requestSuccessListener,errorListener) {            @Override       protected Map<String, String> getParams()  {            Map<String,String> postParms = new HashMap<>();                postParms.put("xxx","xxxx");                return postParms;        }  };

不过看到这儿我总感觉怪怪的,本来是设置参数,提供的却是 getParams方法,而不是set方法,从设计上来说多少有点不合理的感觉。OKhttp对http请求方法的封装从代码设计角度来说就优雅的多了,OKhttp有点严格将请求和请求体分成两个对象Request和RequestBody,有点模仿html form表单请求的味道,想要了解更多的话可以参考《Okhttp发起http请求概述》

到此为止volley系列源码分析就结束了,至于volley的缓存因为其实现原理也很简单,在博主的volley系列中就不做具体解说了

原创粉丝点击