美文网首页
Volley源码解析

Volley源码解析

作者: SDY_0656 | 来源:发表于2017-08-30 16:12 被阅读0次

    volley是现在常用的网络请求库,使用也非常简单,在google的官网上就有一张图介绍volley的请求过程,


    volley-request.png

    本文主要从源码上分析volley的请求过程,达到知其然也知其所以然的目的。
    1,step 1
    在volley使用的最开始都需要调用

    RequestQueue queue = Volley.newRequestQueue(this);
    

    那么这句话的作用到底是什么呢,在源码中是这么写的:

    public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
            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) {
                e.printStackTrace();
            }
    
            if (stack == null) {
                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
                    stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
                }
            }
    
            Network network = new BasicNetwork(stack);
            
            RequestQueue queue;
            if (maxDiskCacheBytes <= -1)
            {
                // No maximum size specified
                queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
            }
            else
            {
                // Disk cache size specified
                queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
            }
    
            queue.start();
    
            return queue;
        }
    

    这段代码非常简单,首先是见一个缓存文件,然后根据SDK的不同版本新建不同的HttpStack,在版本号大于9的时候使用的HurlStack,在后面可以看到也就是使用的HttpUrlConnection来进行网络请求,而在版本号小于9的时候使用的是HttpClientStack,也就是使用的是HttpClient进行网络请求。在然后就新建了一个RequestQueue,下面再看看这个RequestQueue的构造方法和start方法:

        /**
         * 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();
            }
        }
    

    从上面的代码可以看出,volley在启动的时候新建了一个CecheDispatcher和4个NetworkDispatcher,这两个都是继承自Thread,也就是说volley有一个缓存队列和4个网络请求队列,在不停的等待加入队列的请求。在请求队列完成之后,就是要将request加入到请求队列,下面看看请求队列是如何添加请求的,
    RequestQueue添加请求的方法是这样的:

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

    首先将request加入到当前的请求集合中,如何判断是否使用了缓存,如果没有使用请求缓存,那就很简单,直接加入到NetworkQueue中开始请求,如果使用了请求缓存,那么就先看WaitingRequest中是否包含request的cacheKey,如果有,就加入到这个WaitingRequest中,如果没有,那么就将这个request加入到cacheQueue中。现在我们已经知道了如何将request加入到RequestQueue中,那么现在就看看RequestQueue中的一个CacheDispatcher和4个NetworkDispatcher是如何工作的:
    先看NetWorkDispatcher的run方法:

                    // 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");
    

    省略了部分方法,源码也很简单,就是从请求队列中取出一个,如何调用mNetwork请求,获得response后,再对response进行转化。

    再看CacheDispatcher的run方法:

     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();
    
            Request<?> request;
            while (true) {
                // release previous request object to avoid leaking request object when mQueue is drained.
                request = null;
                try {
                    // Take a request from the queue.
                    request = mCacheQueue.take();
                } catch (InterruptedException e) {
                    // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
                try {
                    request.addMarker("cache-queue-take");
    
                    // 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.
                        final Request<?> finalRequest = request;
                        mDelivery.postResponse(request, response, new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    mNetworkQueue.put(finalRequest);
                                } catch (InterruptedException e) {
                                    // Not much we can do about this.
                                }
                            }
                        });
                    }
                } catch (Exception e) {
                    VolleyLog.e(e, "Unhandled exception %s", e.toString());
                }
            }
        }
    

    首先还是从CacheQueue中取出一个request,然后从缓存中检查看是否存在包含request的cacheKey,如果没有,那么就直接加入到NetworkQueue中,如果有,还要看缓存中包含这个cacheKey的缓存是否已经过期,如果过期了,那么还是加入到NetworkQueue中,如果存在这个request的缓存,而且还没有过期,那么就直接从缓存中取出response返回,这个时候还要判断是否需要更新缓存中的 数据,如果需要,那么就把request加入到Networkqueue中再请求一次。
    上面看了如何将request加入到NetworkQueue中,然后NetworkDispatcher就取出Networkqueue中的request进行请求,调用的是Network的performRequest,那么Network是怎么继续请求的呢,在volley中,Network的实现类是BasicNetwork,看看BasicNetwork的performRequest方法:

      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();
    

    这段方法中大多都是一些网络请求细节方面的东西,我们并不需要太多关心,需要注意的是调用了HttpStack的performRequest()方法,这里的HttpStack就是在一开始调用newRequestQueue()方法是创建的实例,默认情况下如果系统版本号大于9就创建的HurlStack对象,否则创建HttpClientStack对象。前面已经说过,这两个对象的内部实际就是分别使用HttpURLConnection和HttpClient来发送网络请求的,我们就不再跟进去阅读了,之后会将服务器返回的数据组装成一个NetworkResponse对象进行返回。
    下面就看一下HurlStack的performRequest方法:

    @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);
            if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
                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;
        }
    

    以上就是volley请求的完整过程,看完源码再看官方给出的流程图,确实清晰多了。

    相关文章

      网友评论

          本文标题:Volley源码解析

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