聊聊Volley源码(网络请求过程)

更多关于安卓源码分析文章,请看:安卓源码分析

Volley源码分析系列:
1.聊下Volley源码(整体流程)
2.聊聊Volley源码(网络请求过程)
3.聊聊Volley源码(缓存流程)

上一篇博文基本溜了一圈Volley的整体流程 聊下Volley源码(整体流程),但是各个细节都没讲到,这篇文章准备讲下网络请求的细节过程。

像上一篇博文所说,每个Volley请求,基本都是创建一个请求队列RequestQueue对象和一个具体的请求(比如StringRequest),然后将该请求add到RequestQueue对象中即可。而在创建请求队列的过程中,又启动了CacheDispatcher(缓存任务分发器)和若干个NetworkDispatcher(网络请求分发器),其实它们都是Thread的子类,而在它们的run方法中,是不断循环取出一个队列(不是RequestQueue哦)的Request去执行相应的任务。今天主要谈下执行网络请求的详细流程。
一切,还得从RequestQueue的add方法说起。

public <T> Request<T> add(Request<T> request) {
        // Tag the request as belonging to this queue and add it to the set of current requests.
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there's already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                // There is already a request in flight. Queue up.
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request<?>>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // Insert 'null' queue for this cacheKey, indicating there is now a request in
                // flight.
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

add并不漫长,直接都复制在这里。首先说明下,RequestQueue 中维护了两个基于优先级的 Request 队列,缓存请求队列和网络请求队列,分别是 PriorityBlockingQueue mCacheQueue和 PriorityBlockingQueue mNetworkQueue,维护了一个正在进行中,尚未完成的请求集合Set mCurrentRequests,
维护了一个等待请求的集合Map mWaitingRequests,如果一个请求正在被处理并且可以被缓存,后续的相同 url 的请求,将进入此等待队列,每个key对应一个相同请求的集合。

在add中,首先将请求与当前的RequestQueue关联起来,然后将请求添加到mCurrentRequests,这意味着请求已经正在进行中。然后给请求设置序列号,因为我们请求是基于优先级加入请求队列的(即优先级高的排在队列前面,更早被取出)。接着判断下请求是否需要缓存,默认的请求都是需要缓存的。如果不需要缓存,则加入到请求队列mNetworkQueue中,并返回add方法。如果需要缓存的,则先提取请求对应的cache key,这里为请求url,然后判断下这个cache key在mWaitingRequests中有没有对应的value,即与请求相同url的请求的队列,有的话则将请求加入到该队列,如果没有的话则将cache key作为key,nul作为value添加入mWaitingRequests中。这里的mWaitingRequests的主要作用,就是避免重复的网络请求,它首先记录第一个新的url请求的url作为key,然后如果在该请求在进行中的时候后续有相同url的请求add到RequestQueue中,则将这些请求添加到对应cache key的队列中就行了。然后,在一个请求不与正在请求的请求的url重复的时候,将该请求添加到mCacheQueue中,add到此结束。

这时你可能会问到,那些默认要缓存的请求,不用添加到网络请求队列么?(反正我当时疑惑了好久= =)简单来讲,就是CacheDispatcher在取出缓存队列的请求的时候,会判断该请求的响应数据是否已经被缓存到本地磁盘,如果没有缓存响应的数据,就会被添加到请求队列中(有缓存的话也要进行新鲜度检测确定缓存是否可用),这部分的细节下一篇博文再讲。

接下来来到NetworkDispatcher的run方法:

 @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // Take a request from the queue.
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

                // If the request was cancelled already, do not perform the
                // network request.
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                // Perform the network request.
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                // If the server returned 304 AND we delivered a response already,
                // we're done -- don't deliver a second identical response.
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // Write to cache if applicable.
                // TODO: Only update cache metadata instead of entire record for 304s.
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // Post the response back.
                request.markDelivered();
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                VolleyError volleyError = new VolleyError(e);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                mDelivery.postError(request, volleyError);
            }
        }
    }

首先会将线程的优先级设置为比普通线程低一点,为的是尽量确保线程不影响UI的体验。然后进入一个死循环,从请求队列中取出请求,然后会判断下请求是否已经被取消,如果取消则对请求调用finish并重新循环取下一个请求(即该请求已经不再处理)。finish的处理就是将请求从RequestQueue的mCurrentRequests和将该请求移除。(这里取消请求,往往运用在和Activity生命周期联动上,在Activity的destroy方法取消掉该Activity的所有请求。主要是防止内存泄漏)

如果是未被取消请求,则调用:

 NetworkResponse networkResponse = mNetwork.performRequest(request);

这里的network默认为BasicNetwork,是主要的执行请求的类,performRequest方法看名字都可以猜出就是在执行请求的方法,它又调用了一个实现HttpStack的类对象去真正执行Http请求。

拿到NetworkResponse之后,网络请求的数据就到手了,封装在这个NetworkResponse对象中。这时候数据还是二进制的,然后对响应的数据进行解析:

Response<?> response = request.parseNetworkResponse(networkResponse);

这是在具体的Request类中要重写的方法,是将二级制数转化为对应的所需要的数据形式。接着如果请求是需要缓存的,就将请求相关数据缓存在磁盘中,mCache默认是DiskBasedCache。

最后执行:

 mDelivery.postResponse(request, response);

这也是上一篇文章说到过的,将响应结果传递到主线程传给客户端代码。
这是run的基本流程,当然在要停止该线程的时候可以退出死循环的,注意这段:

catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

这里是通过中断线程以及设置停止标志位完成线程的停止的。这里调用了RequestQueue的stop方法就会停止所有请求和缓存线程的循环。

再看下请求类BasicNetwork的performRequest方法(省去后面关于请求异常相关处理代码):

public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }

                    // A HTTP 304 response does not have all header fields. We
                    // have to use the header fields from the cache entry plus
                    // the new ones from the response.
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                }

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                        SystemClock.elapsedRealtime() - requestStart);
 ...此处略去若干捕获异常相关代码...

首先会调用addCacheHeaders方法,目的是添加该请求缓存相关的请求头(如果有相同url请求请求过的话,这部分下一篇专门讲缓存再讲,会涉及到Http缓存机制的内容),如果有的话。然后通过真正的请求类HttpStack具体类对像得到HttpResponse对象,这时候就拿到响应数据了。然后获取状态码和响应头,接下来会对状态码做判断确定是否取出缓存数据(这一部分也是涉及Http缓存机制,在下一篇博文介绍),如果不是取缓存则调用:

responseContents = entityToBytes(httpResponse.getEntity());

将响应体转化为二进制数据,接着封装响应体的二进制数据和状态码以及响应头为一个NetworkResponse对象,返回给NetworkDispatcher对象准备传递到客户端主线程。

再来看看这里执行请求的HttpStack具体对像,就像上一篇文章说的,如果是安卓9以上版本,HttpStack为HurlStack,是以安卓的HttpUrlConnection作为请求类的。以HurlStack为例,在执行performRequest的时候,会由url对象获取一个HttpUrlConnection对象,根据Request对象的属性,添加相应的请求方法、请求头、请求参数、请求实体,然后得到HttpUrlConnection的InputStream,在由它得到相应的响应实体、响应头等数据,然后将这些数据封装为一个BasicHttpResponse 返回给BasicNetwork对象。

这时候已经拿到数据了,那么就看下NetworkDispatcher是如何将线程传递到客户端主线程上:

mDelivery.postResponse(request, response);

这里的mDelivery默认是ExecutorDelivery,其实,在我们使用Volley的newRequestQueue方法创建一个RequestQueue的时候,就会调用到RequestQueue的这个构造方法:

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

这里创建了一个ExecutorDelivery对象,并且传入了Looper为主线程Looper的Handler对象(这里如果大家不熟悉Handler的Looper,可以看下我这篇博文 全面分析Handler消息机制

看下ExecutorDelivery的构造方法:

public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

创建了一个Executor的实现类对象,execute方法为调用刚才的Handler的post方法。

传递结果到客户端最终会执行到以下方法:

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

ResponseDeliveryRunnable是一个Runnable,最终会被Handler的post执行到。run方法:

public void run() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }

注意到第十行:

  mRequest.deliverResponse(mResponse.result);

实质上是调用了请求的deliverResponse,这是请求要重写的方法,比如StringRequest的deliverResponse方法:

@Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

是不是豁然开朗呢。是的,mListener就是客户端传入的Response.Listener(请求错误的话是Response.ErrorListener)。

说了这么多,可能比较乱,希望各位看了有所收获吧~~下一篇聊聊Volley源码(缓存流程)

发布了69 篇原创文章 · 获赞 76 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/sinat_23092639/article/details/55673513