Volley源码剖析

>>2020,微服务装逼指南

Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster.

Volley是Google在2013年推出来的HTTP库,旨在帮助开发者更快更简便的实现网络请求。说说为什么要分析Volley的源码吧,因为Volley中线程的转换时通过ThreadHandler来实现的,跟之前的两篇都有着很大的联系(ps:Okhttp和Retrofit都撕不动^_^),哈哈,后面会一步一步的给大家带来Okhttp和Retrofit等更多的源码分析!

执行一个网络请求

我们先整体看下Volley是如何进行一个完整的网络请求的:

1val requestQueue: RequestQueue = Volley.newRequestQueue(context)
2val url = "https://www.baidu.com"
3val request = StringRequest(url,
4        Response.Listener<String> {
5            Log.d("taonce""request result is: $it")
6        },
7        Response.ErrorListener { })
8
9requestQueue.add(request)

上面代码主要做了三件事:

  • 创建一个请求队列RequestQueue : Volley.newRequestQueue(context)

  • 创建一个请求Request:StringRequest(String url, Listenerlistener, @Nullable ErrorListener errorListener)

  • Request加入到RequestQueue:requestQueue.add(request)

接下来通过源码的方法来看看这三步内部做了什么操作。

创建RequestQueue和Request进入Volley.newRequestQueue(context)源码:

进入Volley.newRequestQueue(context)源码:

1public static RequestQueue newRequestQueue(Context context) {
2    // 实际上是调用了另外一个构造方法
3    return newRequestQueue(context, (BaseHttpStack) null);
4}

继续查看newRequestQueue(Context context, BaseHttpStack stack):

 1BasicNetwork network;
2if (stack == null) {
3    // 判断是否大于等于 Android 2.3 版本
4    if (Build.VERSION.SDK_INT >= 9) {
5        // 如果是 Android 2.3 及其以上,就用 HurlStack() 进行网络请求
6        network = new BasicNetwork(new HurlStack());
7    } else {
8        // 如果是 Android 2.3 以下,那么就采用 HttpClientStack(HttpClient) 进行网络请求
9        network = new BasicNetwork(
10                        new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
11    }
12else {
13    // 如果stack不为空,那么就采用传进来的stack
14    network = new BasicNetwork(stack);
15}
16
17return newRequestQueue(context, network);

可以得出:

  • 创建一个BasicNetwork对象

  • stack为空 ----> Android 2.3 及其以上创建HurlStack()对象,并且传给networkHurlStack()采用的是HttpURLConnetion进行网络请求的。

  • stack为空 ----> Android 2.3 以下创建HttpClientStack()对象,并且传给networkHttpClientStack()采用的则是HttpClient进行网络请求,不过现在( 当前版本1.1.1 )new HttpClientStack(HttpClient client)已经被标记了@Deprecated了,因为它采用的HttpClientGoogle 在 Android 6.0 中移除了对 Apache HTTP 客户端的支持,并且从 Android P 开始,`org.apache.legacy` 库将从 `bootclasspath` 中删除。

  • stack不为空 ----> 直接将stack传给network

  • 创建一个RequestQueue()对象并返回

到此为止,大家只要记住上面几个对象就好,接下来会慢慢的讲解对象分别做了什么工作。

这里还是要着重介绍下newRequestQueue()方法:

1private static RequestQueue newRequestQueue(Context context, Network network) {
2    File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
3    // 创建请求队列
4    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
5    queue.start();
6    return queue;
7}

创建RequestQueue()的过程中有一个很重要的点,我们来看看它的构造方法:

 1public RequestQueue(Cache cache, Network network) {
2    // 默认 Network Thread 数目为 DEFAULT_NETWORK_THREAD_POOL_SIZE = 4
3    this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
4}
5
6public RequestQueue(Cache cache, Network network, int threadPoolSize) {
7    this(
8            cache,
9            network,
10            threadPoolSize,
11            // 注意点就是这:创建 Handler 的时候,传递的是 Looper.getMainLooper(),也就是后面将请求结果回调到主线程的关键。
12            new ExecutorDelivery(new Handler(Looper.getMainLooper())));
13}
14
15public RequestQueue(
16        Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery)
 
{
17    mCache = cache;
18    mNetwork = network;
19    mDispatchers = new NetworkDispatcher[threadPoolSize];
20    mDelivery = delivery;
21}

上面第二个构造方法中创建了一个ExecutorDelivery(), 这个对象实现了ResponseDelivery接口的类,用来将网络请求的结果或者缓存中的结果分发到主线程。

再来看看上面queue.start()方法做了什么操作:

 1// 开启5个线程
2public void start() {
3    // 如果5个线程不为空,先停止它们
4    stop(); 
5    // 创建 CacheDispatcher,并开启它
6    mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
7    mCacheDispatcher.start();
8
9    // 创建4个NetworkDispatcher,并开启它们
10    for (int i = 0; i < mDispatchers.length; i++) {
11        NetworkDispatcher networkDispatcher =
12                new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
13        mDispatchers[i] = networkDispatcher;
14        networkDispatcher.start();
15    }
16}

到此,我们前奏分析完了,有一些概念先不急着看,只要知道它的作用就行,后面我们来一个一个击破。

添加请求到请求队列中 : RequestQueue.add( request )

废话不多了,直接进入源码:RequestQueue.add(request)

 1public <T> Request<T> add(Request<T> request) {
2    // 将request和当前的RequestQueue绑定
3    request.setRequestQueue(this);
4    // 将request添加到set集合中,用于执行 cancelAll() 操作
5    synchronized (mCurrentRequests) {
6        mCurrentRequests.add(request);
7    }
8
9    // 给request添加一些标记
10    request.setSequence(getSequenceNumber());
11    request.addMarker("add-to-queue");
12    sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);
13
14    // 判断request是否需要缓存,对于 Get 以外的请求,默认关闭缓存    
15    if (!request.shouldCache()) {
16        mNetworkQueue.add(request);
17        return request;
18    }
19    mCacheQueue.add(request);
20    return request;
21}

添加request的操作算是很简单的了,无非是根据request是否需要缓存,将request添加到CacheQueue或者NetworkQueue中。

看到这,我们是不是在想,怎么添加之后就没有动作了?非也非也,我们回顾下,在创建requestQueue的同时,是不是创建了5个子线程并且开启了它们,它们内部使用的CacheQueue或者NetworkQueue不就是上面提到的么。接下来,我们先看看CacheDispatcher内部做了什么。

CacheDispatcher调度器剖析:

CacheDispatcher就是一个继承了Thread的类,我们在执行RequestQueue.start()的时候创建了它,并开启了它,现在我们来看看它的run()方法:

 1@Override
2public void run() {
3    // 设置线程优先级为后台线程
4    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
5    // 实则是执行了DiskBasedCache的initialize()方法
6    mCache.initialize();
7    while (true) {
8        try {
9            processRequest();
10        } catch (InterruptedException e) {
11            // 中断当前线程,并且退出死循环,mQuit在调用CacheDispatcher的quit()方法之后会被赋值为true
12            if (mQuit) {
13                Thread.currentThread().interrupt();
14                return;
15            }
16        }
17    }
18}

开了一个死循环,然后调用了processRequest(), 我们接着看这个方法:

 1    private void processRequest() throws InterruptedException {
2        // 从CacheQueue中取出一个可用的request
3        final Request<?> request = mCacheQueue.take();
4        processRequest(request);
5    }
6
7    @VisibleForTesting
8    void processRequest(final Request<?> request) throws InterruptedException {
9        request.addMarker("cache-queue-take");
10        request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED);
11
12        try {
13            //request如果被取消了,就直接返回
14            if (request.isCanceled()) {
15                request.finish("cache-discard-canceled");
16                return;
17            }
18
19            Cache.Entry entry = mCache.get(request.getCacheKey());
20            // 没有缓存就把request添加到NetworkQueue中
21            if (entry == null) {
22                request.addMarker("cache-miss");
23                // 没有缓存,并且等待队列中也没有此request,那么就直接加入到NetworkQueue中
24                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
25                    mNetworkQueue.put(request);
26                }
27                return;
28            }
29
30            // 如果缓存过期了,也是一样把request添加到NetworkQueue中
31            if (entry.isExpired()) {
32                request.addMarker("cache-hit-expired");
33                request.setCacheEntry(entry);
34                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
35                    mNetworkQueue.put(request);
36                }
37                return;
38            }
39
40            // 有缓存并且没有过期
41            request.addMarker("cache-hit");
42            // 根据缓存的内容解析
43            Response<?> response =
44                    request.parseNetworkResponse(
45                            new NetworkResponse(entry.data, entry.responseHeaders));
46            request.addMarker("cache-hit-parsed");
47            // 是否需要更新
48            if (!entry.refreshNeeded()) {
49                // 不需要更新,直接将结果调度到主线程
50                mDelivery.postResponse(request, response);
51            } else {
52                request.addMarker("cache-hit-refresh-needed");
53                request.setCacheEntry(entry);
54                response.intermediate = true;
55                // 判断是否有相同缓存键的任务在执行
56                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
57                    // 需要更新结果,先将结果调度到主线程,然后执行new runnable(){}
58                    // runnable中就是将request添加到NetworkQueue中,更新一下内容
59                    mDelivery.postResponse(
60                            request,
61                            response,
62                            new Runnable() {
63                                @Override
64                                public void run() {
65                                    try {
66                                        mNetworkQueue.put(request);
67                                    } catch (InterruptedException e) {
68                                        // Restore the interrupted status
69                                        Thread.currentThread().interrupt();
70                                    }
71                                }
72                            });
73                } else {
74                    // request已经加入到mWaitingRequests中
75                    // 直接把结果调度到主线程
76                    mDelivery.postResponse(request, response);
77                }
78            }
79        } finally {
80            request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
81        }
82    }

我们在processRequest中可以看到有一个方法经常出现,那就是mWaitingRequestManager.maybeAddToWaitingRequests(request),它的作用是判断当前这个request是否有存在相同缓存键的请求已经处于运行状态,如果有,那么就将这个request加入到一个等待队列中,等到相同缓存键的请求完成。

总结一下CacheDispatcher主要步骤:

  • CacheQueue中循环取出request

  • 如果缓存丢失,加入到NetworkQueue中;

  • 如果缓存过期,加入到NetworkQueue中;

  • 将缓存中的数据解析成Response对象;

  • 如果不需要更新,直接将结果回调到主线程,回调操作等介绍完NetworkDispatcher之后一起深入剖析;

  • 如果需要更新,先将结果回调到主线程,然后再将request加入到NetworkQueue中。

NetworkDispatcher调度器剖析:

NetworkDispatcherCacheDispatcher十分类似,都是Thread的子类,下面重点看下它的run()方法:

 1@Override
2public void run() {
3    // 设置线程优先级为后台线程
4    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
5    while (true) {
6        try {
7            processRequest();
8        } catch (InterruptedException e) {
9            // 调用quit()方法之后,mQuit就会被赋值true
10            if (mQuit) {
11                Thread.currentThread().interrupt();
12                return;
13            }
14        }
15    }
16}

继续看processRequest()方法:

 1private void processRequest() throws InterruptedException {
2    // 从NetworkQueue中取出request
3    Request<?> request = mQueue.take();
4    processRequest(request);
5}
6
7@VisibleForTesting
8void processRequest(Request<?> request) {
9    long startTimeMs = SystemClock.elapsedRealtime();
10    request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
11    try {
12        request.addMarker("network-queue-take");
13
14        // 如果request被取消了,那么就不执行此request
15        if (request.isCanceled()) {
16            request.finish("network-discard-cancelled");
17            request.notifyListenerResponseNotUsable();
18            return;
19        }
20
21        addTrafficStatsTag(request);
22
23        // 还记得这个mNetwork么,它就是Volley.newRequestQueue()方法里的BasicNetwork对象,一会我们来看看mNetwork.performRequest()方法是如何得到NetworkResponse的
24        NetworkResponse networkResponse = mNetwork.performRequest(request);
25        request.addMarker("network-http-complete");
26
27        // notModified是服务端返回304,hasHadResponseDelivered()是request已经回调过了
28        if (networkResponse.notModified && request.hasHadResponseDelivered()) {
29            request.finish("not-modified");
30            request.notifyListenerResponseNotUsable();
31            return;
32        }
33
34        // 将NetworkResponse解析成Response对象,在子线程中执行
35        Response<?> response = request.parseNetworkResponse(networkResponse);
36        request.addMarker("network-parse-complete");
37
38        // 将request写入缓存
39        if (request.shouldCache() && response.cacheEntry != null) {
40            mCache.put(request.getCacheKey(), response.cacheEntry);
41            request.addMarker("network-cache-written");
42        }
43
44        request.markDelivered();
45        // 回调结果至主线程
46        mDelivery.postResponse(request, response);
47        request.notifyListenerResponseReceived(response);
48    } 
49    // 以下都是处理异常错误,然后也需要回调至主线程
50    catch (VolleyError volleyError) {
51        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
52        parseAndDeliverNetworkError(request, volleyError);
53        request.notifyListenerResponseNotUsable();
54    } catch (Exception e) {
55        VolleyLog.e(e, "Unhandled exception %s", e.toString());
56        VolleyError volleyError = new VolleyError(e);
57        volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
58        mDelivery.postError(request, volleyError);
59        request.notifyListenerResponseNotUsable();
60    } finally {
61        request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_FINISHED);
62    }
63}

通过NetworkDispatcher.run()方法可以发现,主要分为以下几步:

  • 通过BasicNetwork.performRequest(request)得到NetworkResponse对象;

  • 通过request.parseNetworkResponse(networkResponse)解析得到Response对象;

  • 通过mDelivery将成功结果或者失败结果回调到主线程。

现在我们依次来分析下这三步:

  1. 请求网络,得到NetworkResponse

    BasicNetwork实现了Network接口,其主要代码集中在NetworkResponse performRequest(Request<?> request) throws VolleyError中,也就是执行特定的请求。

     1@Override
    2public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    3   long requestStart = SystemClock.elapsedRealtime();
    4   while (true) {
    5       HttpResponse httpResponse = null;
    6       byte[] responseContents = null;
    7       List<Header> responseHeaders = Collections.emptyList();
    8       try {
    9           // 得到请求头信息
    10           Map<String, String> additionalRequestHeaders =                      getCacheHeaders(request.getCacheEntry());
    11           // 具体的网络请求是靠BaseHttpStack执行的
    12           httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);
    13           int statusCode = httpResponse.getStatusCode();
    14           responseHeaders = httpResponse.getHeaders();
    15           // 下面就是根据不同的状态码返回不同的NetworkResponse对象了,具体就不分析了
    16           if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
    17               Entry entry = request.getCacheEntry();
    18               if (entry == null) {
    19                   return new NetworkResponse(
    20                           HttpURLConnection.HTTP_NOT_MODIFIED,
    21                           /* data= */ null,
    22                           /* notModified= */ true,
    23                           SystemClock.elapsedRealtime() - requestStart,
    24                           responseHeaders);
    25               }
    26           }
    27           // 省略大部分代码...
    28}

    通过上面源码可以看出,BasicNetwork就是封装了一下NetworkResponse对象蛮,并没有涉及到网络请求,我们继续深入到BaseHttpStack.executeRequest(request, additionalRequestHeaders)源码中。

    1public abstract class BaseHttpStack implements HttpStack {
    2   public abstract HttpResponse executeRequest(
    3           Request<?> request, Map<String, String> additionalHeaders)

    4           throws IOException, AuthFailureError
    ;
    5}

    我们发现BaseHttpStack是一个抽象类,那么具体的请求是在哪呢,不知道你是否还有印象,在Volley.newRequestQueue()中,我们创建BasicNetwork的时候是根据 Android 版本传入不同的BaseHttpStack子类,Android 2.3 以上我们传入的是HurlStack对象,下面就看看HurlStack.executeRequest()方法吧。

     1@Override
    2public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
    3       throws IOException, AuthFailureError 
    {
    4   // 得到url
    5   String url = request.getUrl();
    6   HashMap<String, String> map = new HashMap<>();
    7   map.putAll(additionalHeaders);
    8   // Request.getHeaders() takes precedence over the given additional (cache) headers).
    9   map.putAll(request.getHeaders());
    10   ...
    11   URL parsedUrl = new URL(url);
    12   // 通过HttpURLConnection来请求网络
    13   HttpURLConnection connection = openConnection(parsedUrl, request);
    14}

    其实HurlStack.executeRequest()方法里,就是借助了HttpURLConnection对象来请求网络,并根据不同的条件返回不同的HttpResponse对象的。

  2. 解析NetworkResponse, 得到Response

    解析过程是定义在Request抽象类的protected abstract Response<T> parseNetworkResponse(NetworkResponse response)抽象方法中,对于每一种具体的Request类,比如:StringRequestJsonObjectRequest等等都有自己的实现,下面我们来看看StringRequest中的parseNetworkResponse()方法:

     1@Override
    2@SuppressWarnings("DefaultCharset")
    3protected Response<String> parseNetworkResponse(NetworkResponse response) {
    4   String parsed;
    5   try {
    6       // 将response中的data信息取出来
    7       parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    8   } catch (UnsupportedEncodingException e) {
    9       // Since minSdkVersion = 8, we can't call
    10       // new String(response.data, Charset.defaultCharset())
    11       // So suppress the warning instead.
    12       parsed = new String(response.data);
    13   }
    14   // 封装成Response对象
    15   return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    16}
  3. 回调主线程,如果你看过我之前的 Handler源码剖析 文章话,那么这一步就很简单了,我们来理一理:

    回调是通过ResponseDelivery mDelivery对象来执行的,这个对象最早是在创建RequestQueue()的时候初始化的,我在那一小节特意标注了,再来回顾下:

     1public RequestQueue(Cache cache, Network network, int threadPoolSize) {
    2       this(
    3               cache,
    4               network,
    5               threadPoolSize,
    6               // 创建ExecutorDelivery对象,传入一个Handler对象,并且Handler绑定的是主线程的Looper
    7               new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    8}
    9
    10public class ExecutorDelivery implements ResponseDelivery {
    11   private final Executor mResponsePoster;
    12   /**
    13    * ExecutorDelivery构造函数,内部初始化了mResponsePoster接口
    14    * 并且在execute()方法里调用了handler.post()方法
    15    */

    16   public ExecutorDelivery(final Handler handler) {
    17       // 初始化Execute对象
    18       mResponsePoster =
    19               new Executor() {
    20                   @Override
    21                   public void execute(Runnable command) {
    22                       handler.post(command);
    23                   }
    24               };
    25   }
    26}

    知道了它的初始化,我们再来看看它是如何实现回调的:

    Volley 中回调是通过postResponse()方法的 :

     1public void postResponse(Request<?> requestResponse<?> response) {
    2   postResponse(requestresponsenull);
    3}
    4
    5@Override
    6public void postResponse(Request<?> requestResponse<?> response, Runnable runnable) {
    7   request.markDelivered();
    8   request.addMarker("post-response");
    9   mResponsePoster.execute(new ResponseDeliveryRunnable(requestresponse, runnable));
    10}

    postResponse()最终会调用mResponsePoster对象的execute()方法,传入了一个ResponseDeliveryRunnable对象,它实现了Runnable接口,execute()方法会通过Handler.post(runnable)ResponseDeliveryRunnable放入消息队列。最后我们来看看这个ResponseDeliveryRunnablerun()方法在主线程中做了什么操作:

     1@SuppressWarnings("unchecked")
    2@Override
    3public void run() {
    4
    5   // If this request has canceled, finish it and don't deliver.
    6   if (mRequest.isCanceled()) {
    7       mRequest.finish("canceled-at-delivery");
    8       return;
    9   }
    10
    11   if (mResponse.isSuccess()) {
    12       // 执行成功的回调,在具体的Request实现类中,比如StringRequest就会调用listener.onResponse(string)回调
    13       mRequest.deliverResponse(mResponse.result);
    14   } else {
    15       // 执行失败的回调,在request中,直接回调了listener.onErrorResponse(error)
    16       mRequest.deliverError(mResponse.error);
    17   }
    18
    19   // intermediate默认为false,但是在CacheDispatcher的run()中,如果需要更新缓存,那么就会置为true
    20   if (mResponse.intermediate) {
    21       mRequest.addMarker("intermediate-response");
    22   } else {
    23       mRequest.finish("done");
    24   }
    25
    26   // 如果传入了runnable不为空,那就就执行runnable.run()方法
    27   // 回忆下在CacheDispatcher的run()方法中,如果request有缓存,但是需要更新缓存的时候,mDelivery是不是调用的带runnable的方法
    28   if (mRunnable != null) {
    29       mRunnable.run();
    30   }
    31}

    分析到这的时候,大家可以借助下方的流程图理一理Volley的整体流程,主要理解一下缓存线程和网络线程的转换,子线程和主线程的转换:

    Volley源码剖析

    源码分析的文字还在不断的更新,如果本文章你发现的不正确或者不足之处,欢迎你在下方留言或者扫描下方的二维码留言也可!

    Volley源码剖析



原文始发于微信公众号(Taonce):Volley源码剖析