Volley框架的基本解读

Posted zero-27

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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();
    

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


以上是关于Volley框架的基本解读的主要内容,如果未能解决你的问题,请参考以下文章

Volley框架的基本解读

Volley框架的基本解读

Volley框架的基本解读

Volley框架的基本解读

Volley框架的基本解读

Volley源码解读(上)