Android网络框架-Volley(三) CacheDispatcher和NetworkDispatcher源码分析

Android网络框架-Volley(一) 工作原理分析 中的流程图中我们知道Volley工作是有三个线程的:主线程、缓存线程和网络线程。这些线程的工作是建立在RequestQueue上的,上一篇文章 Android网络框架-Volley(二) RequestQueue源码分析以及建立一个RequestQueue 中我们分析了RequestQueue的源码以及最佳使用方式,这篇文章我们来分析两大管家CacheDispatcher和NetworkDispatcher

当一个request加入到RequestQeue中后,首先是交给CacheDispatcher的,我们分析一个CacheDispatcher的源码

public class CacheDispatcher extends Thread {

    //缓存队列
    private final BlockingQueue mCacheQueue;

    //网络队列
    private final BlockingQueue mNetworkQueue;

    //cache实例,从cache中取出的数据是一个Entry
    private final Cache mCache;

    //将数据交给主线程
    private final ResponseDelivery mDelivery;


    /**
     *构造方法
     *
     * @param cacheQueue 缓存队列
     * @param networkQueue 网络队列
     * @param cache Cache
     * @param delivery 分发者(用于将数据分发给主线程)
     */
    public CacheDispatcher(
            BlockingQueue cacheQueue, BlockingQueue networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * 强制退出
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }
    //CacheDispatcher是一个线程,在RequestQueue中的start()方法中调用了
    //CacheDispatcher的start()方法,之后会执行run()方法
    @Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        //设置一个优先级,这是标准的后台线程优先级,稍微比普通优先级低一些
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // 阻塞初始化cache实例
        mCache.initialize();
        //缓存线程开始循环执行
        while (true) {
            try {
                // 阻塞方法,直到从缓存队列中取出一个request,才会往下执行
                final Request request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                // 如果这个request已经被取消了,那么就不往下执行了
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // 从cache中取出数据,取出的数据是一个Entry。里面保存了数据还有一些
                //头部信息
                Cache.Entry entry = mCache.get(request.getCacheKey());
                //如果entry为空,说明缓存中不存在此数据
                if (entry == null) {
                    request.addMarker("cache-miss");
                    //直接加入到网络队列中,从网络中去获取数据
                    mNetworkQueue.put(request);
                    continue;
                }

                // 如果entry完全过期了,那就加入到网络队列中,重新去获取数据
                //判断过期的方法为entry中的ttl<当前系统时间,则为过期
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // cache命中了,把entry中保存的data和头部信息解析出来
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");
                //没有过期,也不需要刷新,将他交付给主线程显示出来。
                if (!entry.refreshNeeded()) {
                    //由delivery交付给主线程
                    mDelivery.postResponse(request, response);
                } else {
                    // entry过期但没有完全过期,是需要刷新的状态,那么就先将其
                    //交付给主线程显示出来,然后再加入到网络线程中,去获取最新数据
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // 将response标记为待完成状态,需要更新
                    response.intermediate = true;

                    // 又delivery将数据分发给主线程之后再加入到网络队列中去
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                //加入到网络队列中去
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }
}

有几种情况会吧request加入到网络队列中去获取数据 1.缓存中没有数据,直接到网络中去获取 2.缓存中有数据,但是已经完全过期,直接到网络中去获取 3.缓存中有数据,已经过期但没有完全过期,是一个需要刷新的状态,先将数据交给主线程,再到网络中获取最新数据,然后刷新 接下来我们分析NetworkDispatcher的源码

public class NetworkDispatcher extends Thread {
    //需要处理的request的队列
    private final BlockingQueue mQueue;
    //处理网络数据的接口
    private final Network mNetwork;
    //cache实例
    private final Cache mCache;
    //分发者,将获取到的数据分发给主线程
    private final ResponseDelivery mDelivery;

    /**
     * 构造方法
     *
     * @param queue待处理的request的队列
     * @param network 处理请求的网络接口
     * @param cache Cache 
     * @param delivery 分发者
     */
    public NetworkDispatcher(BlockingQueue queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     *强制退出
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }
    //和CacheDispatcher一样,在RequestQueue中调用start()方法后,调用了
    //NetworkDispatcher的start()方法,即调用了这个run()方法
    @Override
    public void run() {
        //设置优先级,标准后台线程优先级,略低于普通线程
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request request;
        while (true) {
            try {
                // 阻塞方法,从request队列中取出要执行的request
                request = mQueue.take();
            } catch (InterruptedException e) {
                // 判断是否要退出
                if (mQuit) {
                    return;
                }
                continue;
            }

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

                //如果当前request已经取消,则不继续执行
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                // 如果当前手机版本是4.0以上的,则给该request设置一个tag作为标记
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                }

                // 处理网络请求,返回响应数据
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                // 如果服务器返回304,或者我们已经请求过一次了,则不再去请求数据
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    //finish掉这个request
                    request.finish("not-modified");
                    continue;
                }

                // 在工作线程中去解析响应数据
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                //如果可以缓存,则缓存到cache中
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // 给request一个标记,标记为该request已经被处理完,分发到主线程中
                request.markDelivered();
                //由delicery将最后的数据交付给主线程
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                mDelivery.postError(request, new VolleyError(e));
            }
        }
    }
}

总结: 1.当一个request加入到requestQueue中,首先会交到CacheDispatcher手中。     1.1.request请求的数据在缓存中         1.1.1.数据已经完全过期,则将request交给NetworkDispatcher去从网络获取数据。执行2         1.1.2.数据已经过期,但没有完全过期,需要刷新,则先将数据交付给主线程去显示,再交给NetworkDispatcher去从网路上获取最新数据,然后再交给主线程去更新。执行2         1.1.3.数据没有过期,则直接从缓存中获取数据后,交付给主线程去显示     1.2.request请求的数据没在缓存中,则交给NetworkDispatcher去从网络上获取。执行2 2.此时NetworkDispatcher拿到了request,到网络中去获取数据     2.1.数据可以写到缓存,则将数据写到缓存,并且交付给主线程去显示     2.2.数据不可以写到缓存,则直接交付给主线程去显示      

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/nugongahou110/article/details/46861753
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞