Volley源码解析(三)

在《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 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系列中就不做具体解说了

你可能感兴趣的:(Volley源码解析(三))