美文网首页
Volley源码流程浅析

Volley源码流程浅析

作者: Sanisy | 来源:发表于2016-10-29 00:08 被阅读36次

    我们先从Volley的入口分析,使用Volley的步骤:

     //创建的请求队列
     RequestQueue requestQueue = Volley.newRequestQueue(mContext);
     //创建一个请求
     Request request = new XXRequest(...);
     //往队列添加请求
     requestQueue.add(request);
    

    我们先来看newRequestQueue(Context)方法:

      public static RequestQueue newRequestQueue(Context context) {
            return newRequestQueue(context, null);
      }
    

    调用的是重载的方法:

      public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
            //默认的缓存目录
            File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
    
            String userAgent = "volley/0";
            try {
                String packageName = context.getPackageName();
                PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
                userAgent = packageName + "/" + info.versionCode;
            } catch (NameNotFoundException e) {
            }
    
            if (stack == null) {
                 //如果SDK >= 9 则采用HurlStack来做底层的网络请求
                if (Build.VERSION.SDK_INT >= 9) {
                    stack = new HurlStack();
                } else {
                    // Prior to Gingerbread, HttpUrlConnection was unreliable.
                    // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                    //SDK < 9则采用HttpClientStack来做底层的网络请求
                    stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
                }
            }
    
            //将HttpStack包装成一个Network
            Network network = new BasicNetwork(stack);
            //进一步包装Network,构建一个RequestQueue对象
            RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
            //调用RequestQueue的start方法来开启请求调度
            queue.start();
    
            return queue;
       }
    

    我们可以看到默认的缓存目录是context.getCacheDir()/volley,HttpStack是干什么用的呢?

    /**
     * An HTTP stack abstraction.
     */
    public interface HttpStack {
        /**
         * Performs an HTTP request with the given parameters.
         *
         * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
         * and the Content-Type header is set to request.getPostBodyContentType().</p>
         *
         * @param request the request to perform
         * @param additionalHeaders additional headers to be sent together with
         *         {@link Request#getHeaders()}
         * @return the HTTP response
         */
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError;
    
    }
    

    HttpStack是一个接口,根据注释我们可以知道HttpStack是用于执行HTTP请求的.HttpClientStack的具体实现是HttpClient,网络请求都是通过HttpClient来进行处理的,而HurlStack的具体实现是HttpURLConnection,所有的网络请求都是交给HttpURLConnection去处理的。

    
    /**
     * An HttpStack that performs request over an {@link HttpClient}.
     */
    public class HttpClientStack implements HttpStack {
        protected final HttpClient mClient;
    
        private final static String HEADER_CONTENT_TYPE = "Content-Type";
    
        public HttpClientStack(HttpClient client) {
            mClient = client;
        }
    
        private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
            for (String key : headers.keySet()) {
                httpRequest.setHeader(key, headers.get(key));
            }
        }
    
        @SuppressWarnings("unused")
        private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
            List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
            for (String key : postParams.keySet()) {
                result.add(new BasicNameValuePair(key, postParams.get(key)));
            }
            return result;
        }
        
        /*
         *执行请求的方法
         */
        @Override
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
                throws IOException, AuthFailureError {
            HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
            addHeaders(httpRequest, additionalHeaders);
            addHeaders(httpRequest, request.getHeaders());
            onPrepareRequest(httpRequest);
            HttpParams httpParams = httpRequest.getParams();
            int timeoutMs = request.getTimeoutMs();
            // TODO: Reevaluate this connection timeout based on more wide-scale
            // data collection and possibly different for wifi vs. 3G.
            HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
            HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
            return mClient.execute(httpRequest);
        }
    
        /**
         * Creates the appropriate subclass of HttpUriRequest for passed in request.
         */
        @SuppressWarnings("deprecation")
        /* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
                Map<String, String> additionalHeaders) throws AuthFailureError {
            switch (request.getMethod()) {
                case Method.DEPRECATED_GET_OR_POST: {
                    // This is the deprecated way that needs to be handled for backwards compatibility.
                    // If the request's post body is null, then the assumption is that the request is
                    // GET.  Otherwise, it is assumed that the request is a POST.
                    byte[] postBody = request.getPostBody();
                    if (postBody != null) {
                        HttpPost postRequest = new HttpPost(request.getUrl());
                        postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
                        HttpEntity entity;
                        entity = new ByteArrayEntity(postBody);
                        postRequest.setEntity(entity);
                        return postRequest;
                    } else {
                        return new HttpGet(request.getUrl());
                    }
                }
                case Method.GET:
                    return new HttpGet(request.getUrl());
                case Method.DELETE:
                    return new HttpDelete(request.getUrl());
                case Method.POST: {
                    HttpPost postRequest = new HttpPost(request.getUrl());
                    postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                    setEntityIfNonEmptyBody(postRequest, request);
                    return postRequest;
                }
                case Method.PUT: {
                    HttpPut putRequest = new HttpPut(request.getUrl());
                    putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                    setEntityIfNonEmptyBody(putRequest, request);
                    return putRequest;
                }
                case Method.HEAD:
                    return new HttpHead(request.getUrl());
                case Method.OPTIONS:
                    return new HttpOptions(request.getUrl());
                case Method.TRACE:
                    return new HttpTrace(request.getUrl());
                case Method.PATCH: {
                    HttpPatch patchRequest = new HttpPatch(request.getUrl());
                    patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                    setEntityIfNonEmptyBody(patchRequest, request);
                    return patchRequest;
                }
                default:
                    throw new IllegalStateException("Unknown request method.");
            }
        }
    
        private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
                Request<?> request) throws AuthFailureError {
            byte[] body = request.getBody();
            if (body != null) {
                HttpEntity entity = new ByteArrayEntity(body);
                httpRequest.setEntity(entity);
            }
        }
    
        /**
         * Called before the request is executed using the underlying HttpClient.
         *
         * <p>Overwrite in subclasses to augment the request.</p>
         */
        protected void onPrepareRequest(HttpUriRequest request) throws IOException {
            // Nothing.
        }
    
        /**
         * The HttpPatch class does not exist in the Android framework, so this has been defined here.
         */
        public static final class HttpPatch extends HttpEntityEnclosingRequestBase {
    
            public final static String METHOD_NAME = "PATCH";
    
            public HttpPatch() {
                super();
            }
    
            public HttpPatch(final URI uri) {
                super();
                setURI(uri);
            }
    
            /**
             * @throws IllegalArgumentException if the uri is invalid.
             */
            public HttpPatch(final String uri) {
                super();
                setURI(URI.create(uri));
            }
    
            @Override
            public String getMethod() {
                return METHOD_NAME;
            }
    
        }
    }
    
    public class HurlStack implements HttpStack {
    
        private static final String HEADER_CONTENT_TYPE = "Content-Type";
    
        /**
         * An interface for transforming URLs before use.
         */
        public interface UrlRewriter {
            /**
             * Returns a URL to use instead of the provided one, or null to indicate
             * this URL should not be used at all.
             */
            public String rewriteUrl(String originalUrl);
        }
    
        private final UrlRewriter mUrlRewriter;
        private final SSLSocketFactory mSslSocketFactory;
    
        public HurlStack() {
            this(null);
        }
    
        /**
         * @param urlRewriter Rewriter to use for request URLs
         */
        public HurlStack(UrlRewriter urlRewriter) {
            this(urlRewriter, null);
        }
    
        /**
         * @param urlRewriter Rewriter to use for request URLs
         * @param sslSocketFactory SSL factory to use for HTTPS connections
         */
        public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
            mUrlRewriter = urlRewriter;
            mSslSocketFactory = sslSocketFactory;
        }
    
        @Override
        public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
                throws IOException, AuthFailureError {
            String url = request.getUrl();
            HashMap<String, String> map = new HashMap<String, String>();
            map.putAll(request.getHeaders());
            map.putAll(additionalHeaders);
            if (mUrlRewriter != null) {
                String rewritten = mUrlRewriter.rewriteUrl(url);
                if (rewritten == null) {
                    throw new IOException("URL blocked by rewriter: " + url);
                }
                url = rewritten;
            }
            URL parsedUrl = new URL(url);
            HttpURLConnection connection = openConnection(parsedUrl, request);
            for (String headerName : map.keySet()) {
                connection.addRequestProperty(headerName, map.get(headerName));
            }
            setConnectionParametersForRequest(connection, request);
            // Initialize HttpResponse with data from the HttpURLConnection.
            ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
            int responseCode = connection.getResponseCode();
            if (responseCode == -1) {
                // -1 is returned by getResponseCode() if the response code could not be retrieved.
                // Signal to the caller that something was wrong with the connection.
                throw new IOException("Could not retrieve response code from HttpUrlConnection.");
            }
            StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                    connection.getResponseCode(), connection.getResponseMessage());
            BasicHttpResponse response = new BasicHttpResponse(responseStatus);
            response.setEntity(entityFromConnection(connection));
            for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
                if (header.getKey() != null) {
                    Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                    response.addHeader(h);
                }
            }
            return response;
        }
    
        /**
         * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
         * @param connection
         * @return an HttpEntity populated with data from <code>connection</code>.
         */
        private static HttpEntity entityFromConnection(HttpURLConnection connection) {
            BasicHttpEntity entity = new BasicHttpEntity();
            InputStream inputStream;
            try {
                inputStream = connection.getInputStream();
            } catch (IOException ioe) {
                inputStream = connection.getErrorStream();
            }
            entity.setContent(inputStream);
            entity.setContentLength(connection.getContentLength());
            entity.setContentEncoding(connection.getContentEncoding());
            entity.setContentType(connection.getContentType());
            return entity;
        }
    
        /**
         * Create an {@link HttpURLConnection} for the specified {@code url}.
         */
        protected HttpURLConnection createConnection(URL url) throws IOException {
            return (HttpURLConnection) url.openConnection();
        }
    
        /**
         * Opens an {@link HttpURLConnection} with parameters.
         * @param url
         * @return an open connection
         * @throws IOException
         */
        private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
            HttpURLConnection connection = createConnection(url);
    
            int timeoutMs = request.getTimeoutMs();
            connection.setConnectTimeout(timeoutMs);
            connection.setReadTimeout(timeoutMs);
            connection.setUseCaches(false);
            connection.setDoInput(true);
    
            // use caller-provided custom SslSocketFactory, if any, for HTTPS
            if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
                ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
            }
    
            return connection;
        }
    
        @SuppressWarnings("deprecation")
        /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
                Request<?> request) throws IOException, AuthFailureError {
            switch (request.getMethod()) {
                case Method.DEPRECATED_GET_OR_POST:
                    // This is the deprecated way that needs to be handled for backwards compatibility.
                    // If the request's post body is null, then the assumption is that the request is
                    // GET.  Otherwise, it is assumed that the request is a POST.
                    byte[] postBody = request.getPostBody();
                    if (postBody != null) {
                        // Prepare output. There is no need to set Content-Length explicitly,
                        // since this is handled by HttpURLConnection using the size of the prepared
                        // output stream.
                        connection.setDoOutput(true);
                        connection.setRequestMethod("POST");
                        connection.addRequestProperty(HEADER_CONTENT_TYPE,
                                request.getPostBodyContentType());
                        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
                        out.write(postBody);
                        out.close();
                    }
                    break;
                case Method.GET:
                    // Not necessary to set the request method because connection defaults to GET but
                    // being explicit here.
                    connection.setRequestMethod("GET");
                    break;
                case Method.DELETE:
                    connection.setRequestMethod("DELETE");
                    break;
                case Method.POST:
                    connection.setRequestMethod("POST");
                    addBodyIfExists(connection, request);
                    break;
                case Method.PUT:
                    connection.setRequestMethod("PUT");
                    addBodyIfExists(connection, request);
                    break;
                case Method.HEAD:
                    connection.setRequestMethod("HEAD");
                    break;
                case Method.OPTIONS:
                    connection.setRequestMethod("OPTIONS");
                    break;
                case Method.TRACE:
                    connection.setRequestMethod("TRACE");
                    break;
                case Method.PATCH:
                    connection.setRequestMethod("PATCH");
                    addBodyIfExists(connection, request);
                    break;
                default:
                    throw new IllegalStateException("Unknown method type.");
            }
        }
    
        private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
                throws IOException, AuthFailureError {
            byte[] body = request.getBody();
            if (body != null) {
                connection.setDoOutput(true);
                connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
                DataOutputStream out = new DataOutputStream(connection.getOutputStream());
                out.write(body);
                out.close();
            }
        }
    }
    

    刚开始的时候我们知道,HttpStack是通过BasicNetwork封装成了一个Network对象,我们来看看BasicNetwork里面到底做了啥事情。

    /**
     * A network performing Volley requests over an {@link HttpStack}.
     */
    public class BasicNetwork implements Network {
        protected static final boolean DEBUG = VolleyLog.DEBUG;
    
        private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
    
        private static int DEFAULT_POOL_SIZE = 4096;
    
        protected final HttpStack mHttpStack;
    
        protected final ByteArrayPool mPool;
    
        /**
         * @param httpStack HTTP stack to be used
         */
        public BasicNetwork(HttpStack httpStack) {
            // If a pool isn't passed in, then build a small default pool that will give us a lot of
            // benefit and not use too much memory.
            this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
        }
    
        /**
         * @param httpStack HTTP stack to be used
         * @param pool a buffer pool that improves GC performance in copy operations
         */
        public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
            mHttpStack = httpStack;
            mPool = pool;
        }
    
        @Override
        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);
                } catch (SocketTimeoutException e) {
                    attemptRetryOnException("socket", request, new TimeoutError());
                } catch (ConnectTimeoutException e) {
                    attemptRetryOnException("connection", request, new TimeoutError());
                } catch (MalformedURLException e) {
                    throw new RuntimeException("Bad URL " + request.getUrl(), e);
                } catch (IOException e) {
                    int statusCode = 0;
                    NetworkResponse networkResponse = null;
                    if (httpResponse != null) {
                        statusCode = httpResponse.getStatusLine().getStatusCode();
                    } else {
                        throw new NoConnectionError(e);
                    }
                    VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                    if (responseContents != null) {
                        networkResponse = new NetworkResponse(statusCode, responseContents,
                                responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                        if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                                statusCode == HttpStatus.SC_FORBIDDEN) {
                            attemptRetryOnException("auth",
                                    request, new AuthFailureError(networkResponse));
                        } else {
                            // TODO: Only throw ServerError for 5xx status codes.
                            throw new ServerError(networkResponse);
                        }
                    } else {
                        throw new NetworkError(networkResponse);
                    }
                }
            }
        }
        /**省略代码*/
    

    我们主要来看实现的performRequest方法,可以看到将请求头和请求交给了HttpStack进行了处理,调用的是HttpStackperformRequest(request,headers);并且将请求结果封装成NetworkResponse并返回,但是BasicNetwork的performRequest是什么时候被调用的呢?,我们进一步去看RequestQueue的实现。

    /**
     * A request dispatch queue with a thread pool of dispatchers.
     *
     * Calling {@link #add(Request)} will enqueue the given Request for dispatch,
     * resolving from either cache or network on a worker thread, and then delivering
     * a parsed response on the main thread.
     */
    public class RequestQueue {
    
        /** Callback interface for completed requests. */
        public static interface RequestFinishedListener<T> {
            /** Called when a request has finished processing. */
            public void onRequestFinished(Request<T> request);
        }
    
        /** Used for generating monotonically-increasing sequence numbers for requests. */
        private AtomicInteger mSequenceGenerator = new AtomicInteger();
    
        /**
         * Staging area for requests that already have a duplicate request in flight.
         *
         * <ul>
         *     <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
         *          key.</li>
         *     <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
         *          is <em>not</em> contained in that list. Is null if no requests are staged.</li>
         * </ul>
         */
        private final Map<String, Queue<Request<?>>> mWaitingRequests =
                new HashMap<String, Queue<Request<?>>>();
    
        /**
         * The set of all requests currently being processed by this RequestQueue. A Request
         * will be in this set if it is waiting in any queue or currently being processed by
         * any dispatcher.
         */
        private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
        //需要缓存的请求的队列
        /** The cache triage queue. */
        private final PriorityBlockingQueue<Request<?>> mCacheQueue =
            new PriorityBlockingQueue<Request<?>>();
        //网络请求队列
        /** The queue of requests that are actually going out to the network. */
        private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
            new PriorityBlockingQueue<Request<?>>();
        //默认开启的网络请求线程数量
        /** Number of network request dispatcher threads to start. */
        private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    
        /** Cache interface for retrieving and storing responses. */
        private final Cache mCache;
    
        /** Network interface for performing requests. */
        private final Network mNetwork;
    
        /** Response delivery mechanism. */
        private final ResponseDelivery mDelivery;
        //网络请求调度线程组
        /** The network dispatchers. */
        private NetworkDispatcher[] mDispatchers;
        //缓存请求调度线程
        /** The cache dispatcher. */
        private CacheDispatcher mCacheDispatcher;
    
        private List<RequestFinishedListener> mFinishedListeners =
                new ArrayList<RequestFinishedListener>();
    
        /**
         * Creates the worker pool. Processing will not begin until {@link #start()} is called.
         *
         * @param cache A Cache to use for persisting responses to disk
         * @param network A Network interface for performing HTTP requests
         * @param threadPoolSize Number of network dispatcher threads to create
         * @param delivery A ResponseDelivery interface for posting responses and errors
         */
        public RequestQueue(Cache cache, Network network, int threadPoolSize,
                ResponseDelivery delivery) {
            mCache = cache;
            mNetwork = network;
            mDispatchers = new NetworkDispatcher[threadPoolSize];
            mDelivery = delivery;
        }
    
        /**
         * Creates the worker pool. Processing will not begin until {@link #start()} is called.
         *
         * @param cache A Cache to use for persisting responses to disk
         * @param network A Network interface for performing HTTP requests
         */
        public RequestQueue(Cache cache, Network network) {
            this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
        }
    
        //开始请求调度
        /**
         * Starts the dispatchers in this queue.
         */
        public void start() {
            stop();  // Make sure any currently running dispatchers are stopped.
            // Create the cache dispatcher and start it.
            mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
            mCacheDispatcher.start();
    
            // Create network dispatchers (and corresponding threads) up to the pool size.
            for (int i = 0; i < mDispatchers.length; i++) {
                NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                        mCache, mDelivery);
                mDispatchers[i] = networkDispatcher;
                networkDispatcher.start();
            }
        }
    
        //停止所有的线程,包括缓存线程和网络请求线程
        /**
         * Stops the cache and network dispatchers.
         */
        public void stop() {
            if (mCacheDispatcher != null) {
                mCacheDispatcher.quit();
            }
            for (int i = 0; i < mDispatchers.length; i++) {
                if (mDispatchers[i] != null) {
                    mDispatchers[i].quit();
                }
            }
        }
    
        //添加请求的方法
        /**
         * Adds a Request to the dispatch queue.
         * @param request The request to service
         * @return The passed-in request
         */
        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) {
                //需要进行缓存,并且已经有相同cacheKey存储与等待队列中,那么将请求用链表装载,然后添加进等待队列
                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);
                    //等待队列中没有该cacheKey,则直接添加进缓存队列
                    mCacheQueue.add(request);
                }
                return request;
            }
        }
    }
    

    主要来看add(Request<?>),start以及stop方法。
    add方法
    首先判断是否是需要缓存的请求,如果是,则进一步判断等待队列中是否已经存在过同样cacheKey的请求,如果存在,那么将所有包含该cacheKey的请求封装成一个LinkedList添加进等待请求队列。等待队列中的请求能被调度的条件是:原来与该cacheKey一致的请求已经被取消,或者服务器返回的是304状态码并且原来的请求已经得到了响应,满足条件的才会将等待请求队列中的所有请求添加进缓存请求队列mCacheQueue。如果不存在,那么将请求添加进缓存请求队列,等待线程来调度。

    start方法

    public void start() {
            stop();  // Make sure any currently running dispatchers are stopped.
            // Create the cache dispatcher and start it.
            mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
            mCacheDispatcher.start();
    
            // Create network dispatchers (and corresponding threads) up to the pool size.
            for (int i = 0; i < mDispatchers.length; i++) {
                NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                        mCache, mDelivery);
                mDispatchers[i] = networkDispatcher;
                networkDispatcher.start();
            }
        }
    

    该方法主要创建了一个缓存调度线程和4个网络调度线程,并且启动了这些线程然后等待请求的到来。start方法是创建并启动线程,那么stop方法应该就是停止掉这些线程了。

        /**
         * Stops the cache and network dispatchers.
         */
        public void stop() {
            if (mCacheDispatcher != null) {
                mCacheDispatcher.quit();
            }
            for (int i = 0; i < mDispatchers.length; i++) {
                if (mDispatchers[i] != null) {
                    mDispatchers[i].quit();
                }
            }
        }
    

    接下来分析CacheDispatcher和NetworkDispatcher,看看是如何调度网络请求的。由于是线程我们最主要关注的点是run方法。
    CacheDispatcher的run方法代码:

    @Override
        public void run() {
            if (DEBUG) VolleyLog.v("start new dispatcher");
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    
            // Make a blocking call to initialize the cache.
            mCache.initialize();
    
            while (true) {
                try {
                    // Get a request from the cache triage queue, blocking until
                    // at least one is available.
                    final Request<?> request = mCacheQueue.take();
                    request.addMarker("cache-queue-take");
                    //上面已经说过如果cancel了,那么就会将等待请求队列的请求(具有相同cacheKey的请求)添加进mCacheQueue
                    // If the request has been canceled, don't bother dispatching it.
                    if (request.isCanceled()) {
                        request.finish("cache-discard-canceled");
                        continue;
                    }
                    //缓存检查
                    // Attempt to retrieve this item from cache.
                    Cache.Entry entry = mCache.get(request.getCacheKey());
                    if (entry == null) {
                        request.addMarker("cache-miss");
                        // Cache miss; send off to the network dispatcher.
                        mNetworkQueue.put(request);
                        continue;
                    }
                    //检查缓存是否已经过期
                    // If it is completely expired, just send it to the network.
                    if (entry.isExpired()) {
                        request.addMarker("cache-hit-expired");
                        request.setCacheEntry(entry);
                        mNetworkQueue.put(request);
                        continue;
                    }
    
                    // We have a cache hit; parse its data for delivery back to the request.
                    request.addMarker("cache-hit");
                    Response<?> response = request.parseNetworkResponse(
                            new NetworkResponse(entry.data, entry.responseHeaders));
                    request.addMarker("cache-hit-parsed");
                    //检查缓存是否需要刷新
                    if (!entry.refreshNeeded()) {
                        // Completely unexpired cache hit. Just deliver the response.
                        mDelivery.postResponse(request, response);
                    } else {
                        // Soft-expired cache hit. We can deliver the cached response,
                        // but we need to also send the request to the network for
                        // refreshing.
                        request.addMarker("cache-hit-refresh-needed");
                        request.setCacheEntry(entry);
    
                        // Mark the response as intermediate.
                        response.intermediate = true;
    
                        // Post the intermediate response back to the user and have
                        // the delivery then forward the request along to the network.
                        mDelivery.postResponse(request, response, new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    mNetworkQueue.put(request);
                                } catch (InterruptedException e) {
                                    // Not much we can do about this.
                                }
                            }
                        });
                    }
    
                } catch (InterruptedException e) {
                    // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
            }
        }
    

    首先进行缓存判断,如果没有缓存,那么直接将请求Request交给网络请求队列mNetworkQueue;如果该请求已经存在缓存中,那么判断缓存是否已经过期,过期同样交给网络请求队列,没过期则判断是否需要刷新缓存,不需要则返回缓存的响应结果,如果需要刷新,会先将缓存的响应结果返回,返回再讲该请求交给网络请求队列去刷新缓存。

        @Override
        public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
            request.markDelivered();
            request.addMarker("post-response");
            mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
        }
    
        private class ResponseDeliveryRunnable implements Runnable {
            private final Request mRequest;
            private final Response mResponse;
            private final Runnable mRunnable;
    
            public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
                mRequest = request;
                mResponse = response;
                mRunnable = runnable;
            }
    
            @SuppressWarnings("unchecked")
            @Override
            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();
                }
           }
        }
    

    NetworkDispatcher

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

    网络请求线程就比较简单了,直接请求,然后判断是否需要刷新缓存(返回304并且缓存已经被返回过了,那么不需要刷新缓存),该请求是一个缓存请求(也就是说shouldCache()为ture,这个由服务器规定max-age,如果该请求在上一次请求的时间+max-age时间 < 当前的系统时间,则不会去向服务器去请求而是直接返回本地的缓存,即shouldCache为false),那么就刷新缓存。最后是响应结果。

    大体的流程如下:

    volley.png

    至于响应是如何回到主线程的呢?我们来看看RequestQueue的其中一个构造方法:

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

    我们可以看到new ExecutorDelivery(new Handler(Looper.getMainLooper()),ExecutorDelivery实现的是ResponseDelivery接口,ResponseDelivery其实就是用于返回请求结果的,看到ExecutorDelivery构造参数Looper.getMainLooper(),我们就应该可以猜到最后的结果是通过Handler机制来传递到主线程的。

    其实现在网络请求库有OkHttp和Retrofit等,如果想要使用OkHttp作为Volley的底层请求处理库的话,需要定义一个类实现HttpStack,并在performRequest方法中使用OkHttp来做相关的网络请求。

    相关文章

      网友评论

          本文标题:Volley源码流程浅析

          本文链接:https://www.haomeiwen.com/subject/rxnzyttx.html