Volley框架的基本解读(五)

在之前四篇博客中,我们已经将RequestQueue中start方法中的网络请求这条主线完全解析了一遍,接下来我们看另一条缓存主线,CacheDispatcher的源码:


public class CacheDispatcher extends Thread

同NetworkDispatcher一样,CacheDispatcher同样是一个线程


public CacheDispatcher(
            BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

里面也只有一个构造方法,四个参数分别是缓存队列,网络队列,缓存处理类,结果分发类,同样的在RequestQueue调用了它的start方法,因为是Thread所以我们看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.
            	// 从缓存队列中取出request
                final Request request = mCacheQueue.take();
                // 调试信息
                request.addMarker("cache-queue-take");

                // If the request has been canceled, don't bother dispatching it.
                // 如果该request以取消,则中断任务
                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解析,并输出调试日志
                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;
            }
        }
    }

该方法首先初始化了缓存数据,让我们进入mCache.initialize方法里面看看,这里说一点Cache同Network一样是一个接口,里面的方法稍微多一点,我们来看具体子类实现,同样是在Volley.newRequestQueue中创建的:


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


接着看


public DiskBasedCache(File rootDirectory) {
        this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
    }


public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
        mRootDirectory = rootDirectory;
        mMaxCacheSizeInBytes = maxCacheSizeInBytes;
    }

rootDirectory是缓存径路,DEFAULT_DISK_USAGE_BYTES默认值是5 * 1024 * 1024,也就是5M,表示最大缓存容量。


我们来看看它的initialize方法:


/**
     * Initializes the DiskBasedCache by scanning for all files currently in the
     * specified root directory. Creates the root directory if necessary.
     * 
     * 初始化缓存,将缓存文件全部读入内存中
     */
    @Override
    public synchronized void initialize() {
    	// 如果文件不存在,说明无缓存
        if (!mRootDirectory.exists()) {
            if (!mRootDirectory.mkdirs()) {
                VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
            }
            return;
        }

        // 获取缓存文件数组
        File[] files = mRootDirectory.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                CacheHeader entry = CacheHeader.readHeader(fis);
                entry.size = file.length();
                putEntry(entry.key, entry);
            } catch (IOException e) {
                if (file != null) {
                   file.delete();
                }
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException ignored) { }
            }
        }
    }

前面很简单,判断缓存路径是否存在,否创建缓存路径,如果创建失败,那么初始化也就失败了。


然后获取缓存路径下的所有缓存文件,用字节流读取解析,CacheHeader是DiskBasedCache中的静态内部类,里面定义了很多字段,比如size数据长度,key缓存的键,其实就是URL,还有serverDate服务器的返回时间等等,readHeader方法就是对读取缓存文件进行了封装,考虑到涉及到的方法太多,我就不一一贴出来了,得到了CacheHeader这个缓存实体,putEntry方法就是将它放入内存:


/**
     * Puts the entry with the specified key into the cache.
     * @param key The key to identify the entry by.
     * @param entry The entry to cache.
     * 
     * 将缓存读入内存
     */
    private void putEntry(String key, CacheHeader entry) {
    	// 如果内存中没有该缓存,累加当前缓存大小,如果存在,计算缓存容量差,再进行累计
        if (!mEntries.containsKey(key)) {
            mTotalSize += entry.size;
        } else {
            CacheHeader oldEntry = mEntries.get(key);
            mTotalSize += (entry.size - oldEntry.size);
        }
        mEntries.put(key, entry);
    }

mEntries是一个Map集合,定义在DiskBasedCache的成员变量中


/** Map of the Key, CacheHeader pairs 初始位16,扩展0.75倍,使用排序*/
    private final Map<String, CacheHeader> mEntries =
            new LinkedHashMap<String, CacheHeader>(16, .75f, true);

initialize方法我们分析完了,回过头继续看CacheDispatcher的run方法,同NetworkDispatcher一样的写法,后面也是一个while死循环,mCacheQueue同样是一个优先级队列,试图从队列中获取一个request,判断request是否取消,是结束该次请求,request.getCacheKey()返回的就是URL,试图从缓存中查找对应的缓存,如果找不到,扔进网络队列中,它就不管了,如果获取到的缓存已经过期,还是扔进网络队列,当个甩手掌柜,到这里可以确定缓存有效,封装成Response准备返回,下面这个!entry.refreshNeeded()对缓存过期的再次判断,是我也不太理解的地方,但代码是很清晰的,如果没有过期,分发结果,如果过期了,它依然会返回结果,但会向网络请求一次,也就是说,该request会得到两个response,一个是缓存的,一个是网络的。


到这里大家可能已经急不可待想揭开mDelivery的神秘面纱了,但我们下一篇再说,在CacheDispatcher还有一个我们熟悉的方法:


/**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     * 
     * 标记退出,并中断线程
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

作用就不必我多说了吧,我们下篇博客再见!


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值