Volley框架解析(三)-----Dispatcher解析

Volley框架解析(三)—–Dispatcher解析


题外话(可直接无视,跳过~

    在上一篇博客中结合volley源代码分析了Volley.javaRequestQueue.java这两个类,不知道代码全贴上去了,会不会看着很烦的嗯= =,当时看源代码的时候心里就有些不淡定,满屏幕看不懂的东西,但是个人觉得源代码和注释一起看才方便,一段代码一段解释会感觉有点摸不着头脑= =。前面的博客中一直提到了Dispatcher这种的东西,有mNetworkDispatchermCacheDispatcher之类的,这篇博客就开始进一步的介绍Volley中的Dispatcher。


1. 简介

    在Volley中涉及到了两类的Dispatcher,一类是涉及到缓存的CacheDispatcher.java,另外一类是用来处理网络方面request的NetworkDispatcher.java,最开始出现这两个Dispatcher是在{#link RequestQueue#start()}中,再来回顾一下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();
    }
}

    先暂停了所有的dispatcher,然后又新建了dispatcher并start()。让dispatcher开始工作,实际上这些dispatcher继承了Thread类,是独立于主线程之外的工作线程,这样可以使RequestQueue中request的加入和处理同时进行。下面就结合CacheDispatcher和NetworkDispatcher两个类的代码来分析,Volley中是如何对request来进行调度和处理的。

2. CacheDispatcher.java

package com.android.volley;

import android.os.Process;

import java.util.concurrent.BlockingQueue;

/**
 * Provides a thread for performing cache triage on a queue of requests.
 * 提供一个用来处理涉及到缓存的requests的线程
 * 
 * Requests added to the specified cache queue are resolved from cache.
 * Any deliverable response is posted back to the caller via a
 * {@link ResponseDelivery}.  Cache misses and responses that require
 * refresh are enqueued on the specified network queue for processing
 * by a {@link NetworkDispatcher}.
 * CacheDispatcher用来处理缓存队列里面(mCacheQueue)中的request
 * 任何符合delivery要求的response都会被通过ResponseDelivery的接口传递给caller
 * 有些cache丢失了或者是cache中的数据需要更新的,都将会交给NetworkDispatcher去处理
 * 交给NetworkDispatcher处理的方法就是直接放到mNetworkQueue中去
 * 因为NetworkDispatcher总是从mNetworkQueue中取出request来进行处理的
 */


public class CacheDispatcher extends Thread {

    private static final boolean DEBUG = VolleyLog.DEBUG;

    /** 
     * The queue of requests coming in for triage. 
     * 将要被处理的涉及到缓存的Request存放在这个阻塞队列里
     * 等着咯,这个mCacheQueue和RequestQueue中的mCacheQueue指向的队列是同一个
     * 也就是说整个Volley在运行的时候只有一个mCacheQueue
     */
    private final BlockingQueue<Request<?>> mCacheQueue;

    /** 
     * The queue of requests going out to the network. 
     * 这个阻塞队里里面存着的可是要去进行网络访问的request
     * 开始还不明白这里不应该是涉及到访问缓存的request
     * 怎么有个这东西出来了,其实看到后面了就会发现,缓存里面有两个过期时间
     * 在后面会介绍到Cache.java类,Cache.Entry类中涉及到了
     * ttl 和 softTtl这两个long型的数据,用来标识缓存是否已经过期了
     * 或者是否需要去检查是否要更新缓存的两个间隔时间
     */
    private final BlockingQueue<Request<?>> mNetworkQueue;

    /** 
     * The cache to read from. 
     * 用于读写缓存的接口
     * 这个接口也是在Volley中只有一个
     * mCacheDispatcher和mNetworkDispatcher公用的
     */
    private final Cache mCache;

    /** 
     * For posting responses. 
     * ResponseDelivery对象引用,用来将request的结果传递给caller
     * 在NetworkDispatcher里面也有出现
     * 这个也是从RequestQueue中传递过来的,公用
     */
    private final ResponseDelivery mDelivery;

    /** 
     * Used for telling us to die. 
     * 直译 : 用来告诉我们去死= = (shit)
     * 然而 : 这个变量用来标志这个dispatcher是否要继续工作下去
     * 如果为true就结束本线程中的死循环
     */
    private volatile boolean mQuit = false;

    /**
     * Creates a new cache triage dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     * 构造函数咯,创建一个存放需要访问缓存的request的调度线程
     * 在创建之后需要将其用start()启动
     * 
     * @param cacheQueue Queue of incoming requests for triage
     * 存放request的缓存队列
     * @param networkQueue Queue to post requests that require network to
     * 存放涉及network的网络队列
     * @param cache Cache interface to use for resolution
     * 用来处理缓存读写问题的接口
     * @param delivery Delivery interface to use for posting responses
     * 用来反馈结果的接口
     */
    public CacheDispatcher(
            BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     * 将标志位mQuit置为true,在每次死循环的最后会判断该标志位
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    /**
     * 前面提到了CacheDispatcher继承了Thread类
     * 这里就重写了run()方法
     * 当外面调用了mCacheDispatcher.start()之后
     * run()里面的方法就开始执行了
     */
    @Override
    public void run() {

        if (DEBUG) VolleyLog.v("start new dispatcher");
        /**
         * 给自己设置了线程的优先级
         * THREAD_PRIORITY_BACKGROUND的优先级是0x0000000a(也就是10)
         * 还有其他的很多种优先级,该优先级处于较高的位置
         */
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        /**
         * Make a blocking call to initialize the cache.
         * 在读写缓存之前做一些初始化工作,例如扫描缓存目录是否存在等
         * 这个暂时先不用管里面的内容,等介绍到Cache.java的时候就会明白
         */
        mCache.initialize();


        /**
         * 从这里开始就进入了死循环的状态
         * 除非出现了什么没有catch的exception
         * 或者是mQuit标志位被置成了true
         * 这个死循环将一直进行下去= =
         * 总感觉有什么不妥的地方,感觉死循环怪怪的噢
         */
        while (true) {
            /**
             * 和NetworkDispatcher里面的流程没有什么太大的变化
             * 还是一个死循环不停的从CacheQueue中取出Request
             */
            try {
                /**
                 * Get a request from the cache triage queue, blocking until
                 * at least one is available.
                 * 从缓存request队列里面取出等待处理的request
                 * 如果没有可取出的request,则会在这里阻塞
                 * 这个是{#link PriorityBlockingQueue#take()}函数的作用
                 * 
                 */
                final Request<?> request = mCacheQueue.take();

                /**
                 * 给每个request添加上一个打log的标志
                 * 为了debug的需要
                 */
                request.addMarker("cache-queue-take");

                /**
                 * If the request has been canceled, don't bother dispatching it.
                 * 如果正在处理的这个请求被取消了
                 * 中断对该request的处理,continue去处理下一个request的调度
                 * 调用{#link Request#finish()}方法,传入的参数是为了debug方便,打出request调度进度的log
                 */
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                /**
                 * 在这里NetworkDispatcher和CacheDispatcher出现了一点差异
                 * NetworkDispatcher.java在这一步就直接开始网络请求了
                 * 
                 * 由于是CacheDispatcher.java,肯定是主要以Cahce为主的
                 * CacheDispatcher在这里先看看有没有缓存
                 * 如果没有缓存则马上将这个request加入到NetworkQueue中
                 * (意思好像就是= =兄弟你排错队了)
                 * 然后继续喊下一个request来被处理
                 */
                // 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;
                }

                /**
                 * 能到这一步的request不简单了
                 * 肯定是被上面的mCache.get(cacheKey)查到了有缓存的(毕竟有靠山的伤不起)
                 * 有缓存还不能太大意= =,万一缓存尼玛是个过期的就惨了= =
                 * 先用entry.isExpired()函数检查一番
                 * 过期了照样还是给我滚到NetworkQueue中去排队
                 *
                 * 继续喊下一个request来
                 */
                // 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;
                }

                /**
                 * 哎哟,能到这一步的request更加不简单了,不仅仅有缓存
                 * 而且还是能用的缓存,没有过期的诶,这才是有真的靠山= =
                 *
                 * 将缓存的信息都拿出来,组成一个NetworkResponse
                 * 就像是刚刚从网络上获取出来的一样,再形成一个Response.java对象
                 * 但是不要着急把这个response直接传回caller,这个response还没确定是否需要refresh
                 */

                // We have a cache hit; parse its data for delivery back to the request.
                request.addMarker("cache-hit");

                /**
                 * 将一个由缓存中的数据创建的NetworkResponse.java对象
                 * 通过{#link Request#parseNetworkResponse()}方法
                 * 来解析成一个Response.java对象
                 */
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));

                //为了方便debug,对request每一个时期的状态都需要添加不同的log信息
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the response.
                    /**
                     * 如果缓存不需要刷新的话,直接传回给caller
                     */
                    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.
                    /**
                     * 如果需要刷新的话,将这个response中的intermediate参数置为true
                     * 然后再传递给caller,
                     * 随后将请求发送到服务器进行刷新
                     */
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    /**
                     * Mark the response as intermediate.
                     * 将这个response标记成中间产物,也就不是最终的response
                     * 
                     */
                    response.intermediate = true;

                    /** 
                     * Post the intermediate response back to the user and have
                     * the delivery then forward the request along to the network.
                     * poseResponse()方法中的Runnable是在response被传递给caller了之后
                     * 再执行的,在ResponseDelivery.java中有注释
                     * 
                     */
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                //将request加入到网络请求队列中去
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                //当cacheQueue中没有request之后就会捕捉到异常
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }
}

3. NetworkDispatcher.java

package com.android.volley;

import android.annotation.TargetApi;
import android.net.TrafficStats;
import android.os.Build;
import android.os.Process;
import android.os.SystemClock;

import java.util.concurrent.BlockingQueue;

/**
 * Provides a thread for performing network dispatch from a queue of requests.
 * 提供一个线程专门用来从请求队列(NetworkQueue)里面调度网络请求
 * 
 * Requests added to the specified queue are processed from the network via a
 * specified {@link Network} interface. Responses are committed to cache, if
 * eligible, using a specified {@link Cache} interface. Valid responses and
 * errors are posted back to the caller via a {@link ResponseDelivery}.
 *
 * 被加入到RequestQueue中的request会被NetWork的接口进一步加工处理.
 * 如果从网络返回的response是符合条件的,则会被添加到缓存中去。
 * 有效的response将通过ResponseDelivery返回给调用者
 */
public class NetworkDispatcher extends Thread {
    /** 
     * The queue of requests to service. 
     * 这个queue就是RequestQueue.java中的mNetworkQueue
     */
    private final BlockingQueue<Request<?>> mQueue;

    /** 
     * The network interface for processing requests. 
     * 处理request的接口,其中的方法是performRequest()
     */
    private final Network mNetwork;     

    /** 
     * The cache to write to. 
     * 处理缓存的接口
     */
    private final Cache mCache;

    /** 
     * For posting responses and errors. 
     * 用来传递response和error的deliver.
     */
    private final ResponseDelivery mDelivery;

    /** 
     * Used for telling us to die. 、
     * 这里使用到了volatile变量
     * 这个volatile类似于final之类的修饰词
     * 是用来保证每次mQuit被读取的时候都是最新的
     * 避免了读取的值和实际变量的值不同的情况
     * 可以参考这篇博客,讲解的比较详细:
     * http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
     */
    private volatile boolean mQuit = false;

    /**
     * Creates a new network dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     * 构造器,用于创建一个新的网络调度线程,必须要调用call来开始处理request
     * 
     * @param queue Queue of incoming requests for triage
     * 等待处理的request队列
     * @param network Network interface to use for performing requests
     * @param cache Cache interface to use for writing responses to cache
     * @param delivery Delivery interface to use for posting responses
     */
    public NetworkDispatcher(BlockingQueue<Request<?>> queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     * 强制调度器立刻退出,不再调度request。
     * 
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    /**
     * 这里涉及到了TrafficStats类,官方解释如下:
     * Class that provides network traffic statistics. 
     * 这个类提供网络流量统计的服务。
     * These statistics include bytes transmitted and received and network packets transmitted and received, 
     * over all interfaces, over the mobile interface, and on a per-UID basis.
     * 这些被统计的流量包括传输的字节数和收到的字节数以及网络数据包
     * These statistics may not be available on all platforms. 
     * If the statistics are not supported by this device, UNSUPPORTED will be returned.
     * 这些数据并不是在所有的平台上都可以用
     * 如果不可用,则会返回UNSPPORTED
     * 貌似是每个request都统计一下网络流量= =
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void addTrafficStatsTag(Request<?> request) {
        // Tag the request (if API >= 14)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
        }
    }

    /**
     * 由于NetworkDispatcher继承自Thread,重写了run()方法
     * 里面的内容都会在另启动一个线程来执行
     * 在CacheDispatcher中有很多相似的地方
     */
    @Override
    public void run() {
        /**
         * 给自己设置了线程的优先级
         * THREAD_PRIORITY_BACKGROUND的优先级是0x0000000a(也就是10)
         * 还有其他的很多种优先级,该优先级处于较高的位置
         */
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        Request<?> request;

        /**
         * 进入了一个死循环状态
         * 开始不停的工作
         */
        while (true) {

            /**
             * elapsedRealtime()函数返回的是线程从启动到现在的总时间
             * 也包括线程睡眠时间在内
             * 单看这一句看不出什么门道,结合在后面的异常处理时会用到startTimeMs
             * 这里是记录一个request开始的时刻点,到后面再次调用elapsedRealtime()
             * 两个变量相减得到了request花费了多长的时间
             */
            long startTimeMs = SystemClock.elapsedRealtime();
            /**
             * release previous request object to avoid leaking request object when mQueue is drained.
             * 释放前面的一个Request对象,以免因为Request对象不停的申请而导致内存泄漏
             */
            request = null;

            /**
             * 尝试着从RequestQueue中取出一个Request,对其进行处理
             * 可能会因为某些原因(可能是队列中没有元素了)会抛出异常
             * 这个时候就捕捉异常并检验是否要退出了,需要退出则return
             * 不需要退出则继续下一次循环,看有没有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;
            }

            /**
             * 到这一步的时候,request应该是指向了一个Request
             * 下面开始向服务器发送这个Request
             */

            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.
                 * 直接调用mNetwork的接口,发送request并获得NetworkResponse
                 */
                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<?> 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.

                /**
                 * 在这里看到作者的TODO了,还能改进的地方就是在出现了返回码是
                 * 304的情况时,只更新缓存中的元数据(也就是response的主体)
                 * 而不是整个cache的记录下来,有些重复的数据可以不用理会.
                 */
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                /**
                 * 将Request.java中的变量mResponseDelivered置成true
                 * 标志着这个request的结果已经传回给了caller
                 */

                request.markDelivered();

                /**
                 * 通过ResponseDelivery的接口将包装好了的Response返回给调用者
                 */
                mDelivery.postResponse(request, response);

            } catch (VolleyError volleyError) {
                /**
                 * 设置了request从队列中取出到服务器出现异常反应
                 * 所花费的时间
                 */
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);

                /**
                 * 将网络请求的错误通过ResponseDelivery传递给调用者
                 * 告诉它这.....不幸的一切
                 */
                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);
            }
        }
    }

    private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
        error = request.parseNetworkError(error);
        mDelivery.postError(request, error);
    }
}

    当时看完了CacheDispatcher.java之后,再看NetworkDispatcher.java的时候,就会觉得这两个类非常的相似,run()函数里面的东西有些都差不多,都是进行一个死循环,从阻塞队列里面取出request,针对不同的情况做出不同的处理。

    从Volley.java暴露给外部的api开始,一直到现在,已经慢慢的了解了Volley中有一个RequestQueue对象,所有的request都在里面排着队等待被处理,RequestQueue里面还有一个CacheDispatcher和几个NetworkDispatcher,分别负责着不同种类的request。接下来要介绍的就是Volley中Network.java接口以及其默认实现类BasicNetwork.java,还有涉及缓存读写的接口Cache.java以及其默认实现类DiskBasedCache.java

    上面的笔记如果有什么错误,还望大家给我指正,小达的qq:2319821734,希望和大家能够共同学习,共同进步~~~。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值