Volley源码分析

作者: NamelessCoder | 来源:发表于2017-05-28 14:34 被阅读0次
    volley-request.png

    Volley的工作原理

    1. 线程管理
    2. 缓存的管理
    3. 发送网络请求过程
      • 在主线程把请求加入请求队列
      • 缓存线程查询请求是否有缓存,如果有缓存,则从缓存中获取数据解析返回给主线程,如果没有缓存,把请求分发给网络线程
      • 网络线程发送请求,从服务器获取数据,解析后返回给主线程

    Volley源码分析

    ​ 首先,从创建请求队列开始看

    Volley.newRequestQueue(context);
    

    ​ 点击进去跳到Volley类,然后就会跳到这个方法里面.

    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  //这里创建了一个缓存目录,具体路径在data/data/包名/cache/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) {
            if (Build.VERSION.SDK_INT >= 9) { //如果当前的构建版本大于SDK9 就用HurlStack为HttpStack赋值,HttpStack内部用HttpUrlConnection来创建网络连接
                stack = new HurlStack();
            } else { //否则就用HttpClientStack来为HttpStack赋值,HttpClientStack里面使用HttpClient来创建网络连接
                // 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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();
    
        return queue;
    } 
    

    ​ 我们进去看一下HttpStack类

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

    ​ 发现这是一个接口,那就看一下他的实现类HurlStack,查看该类的实现方法

    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); //使用HttpURLConnection来打开网络连接
            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;
        }
    

    ​ 我们发现这个方法主要是在构建网络连接,里面是使用HttpURLConnection来网络连接,然后返回HttpResponse响应.

    继续回到newRequestQueue方法里面

        Network network = new BasicNetwork(stack);
    
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();
    

    ​ 代码执行到这里了,我们看一下这行代码,这里传入了两个参数,一个是基于磁盘的缓存,一个是网络连接.进去DiskBasedCache类看一下

     RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    
      public DiskBasedCache(File rootDirectory) {
            this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
        }
    

    ​ 可以看出这里设置了缓存的目录以及默认使用缓存的大小

     private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
    

    ​ 默认缓存的大小为5M.

    我们最后进入RequestQueue里面看一下构造方法做了一些什么事情

    public RequestQueue(Cache cache, Network network) {
            this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
        }
    

    继续点击进去,

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

    再次点击进去,最终是调用这个构造方法

     public RequestQueue(Cache cache, Network network, int threadPoolSize,
                ResponseDelivery delivery) {
            mCache = cache; //根据上面封装的构造方法,这里传进来的是new DiskBasedCache(cacheDir)
            mNetwork = network;//这里传进来的是new BasicNetwork(stack)
            mDispatchers = new NetworkDispatcher[threadPoolSize];//threadPoolSize传进来的是 DEFAULT_NETWORK_THREAD_POOL_SIZE,根据查看源码得知这个值是为4
            //private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
            mDelivery = delivery; 
       //这里传进来的是
       //new ExecutorDelivery(new Handler(Looper.getMainLooper())));
       //这里做的是,将子线程切换到主线程
        }
    

    我们最终是初始化了以上这些类,我们点击NetworkDispatcher进去看一下,里面做了一些什么事情

    发现NetworkDispatcher是继承了Thread类的,就说明,上面初始化的时候,初始化了4个线程.

     RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
            queue.start();
    

    可以看出RequestQueue初始化做了一下事情:

    • 初始化磁盘缓存
    • 初始化了网络连接
    • 创建了4个网络线程
    • 初始化了一个将执行结果发送到主线程的传递器

    继续点击进去看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();
            }
        }
    

    通过这个方法我们可以知道,首先创建了缓存分发器.首先检查缓存中是否有数据,我们进去CacheDispatcher类查看

    这是一个继承了Thread的类,

     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");     //并且为请求添加标记
    
                    // 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;
                }
            }
        }
    

    看完这里,可以知道,首先在内存中寻找缓存,如果没有就到磁盘中寻找缓存,如果没有,再发送网络请求获取数据.

    volley-request.png

    和上面这个图的工作原理一样.数据加入网络请求队列之后,继续往下看

      for (int i = 0; i < mDispatchers.length; i++) {
                NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                        mCache, mDelivery);
                mDispatchers[i] = networkDispatcher;
                networkDispatcher.start();
            }
    

    这里添加了四个线程给网络分发器,对应上图.

    我们进去NetworkDispatcher里面看

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

    工作原理和上面的缓存队列工作原理相似.

      mDelivery.postResponse(request, response); //发送响应到主线程
    

    继续往下查看

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

    这里执行了ResponseDeliveryRunnable,继续查看

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

    响应成功或者失败都会回调监听

    响应成功:

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

    响应失败:

    public void deliverError(VolleyError error) {
            if (mErrorListener != null) {
                mErrorListener.onErrorResponse(error);
            }
        }
    

    因为本人这里是使用JsonRequest,所以这里会响应JsonRequest的请求,监听器就有数据过来了

    private NetWorkListener<NewsListBean> mListener=new NetWorkListener<NewsListBean>()
        {
            @Override
            public void onResponse(NewsListBean response) {
                //保存新闻列表数据
                mNews = response.getData().getNews();
                //保存轮播图数据
                mTopNews = response.getData().getTopnews();
                //刷新新闻列表
                mAdapter.notifyDataSetChanged();
                //保存more字段,加载更多
                mMore = response.getData().getMore();
                List<String> urls = new ArrayList<String>();
                mTopNews = response.getData().getTopnews();
                //遍历轮播图的数据,构建图片url集合
                for (int i = 0; i <mTopNews.size() ; i++) {
                    urls.add(mTopNews.get(i).getTopimage());
                }
                //刷新轮播图
                mFunBanner.setImageUrls(urls);//内部使用glide加载图片
            }
        };
    

    NetWorkListener是我自己写的,实现了Response.Listener,Response.ErrorListener接口

    public class NetWorkListener<T> implements Response.Listener<T>,Response.ErrorListener{
        @Override
        public void onErrorResponse(VolleyError error) {
    
        }
    
        @Override
        public void onResponse(T response) {
    
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Volley源码分析

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