Volley学习(RequestQueue分析)

Volley的RequestQueue用来缓存请求处理器CacheDispatch和网络请求处理器NetworkDispatch来处理Request的。当我们调用RequestQueue.start()是,两个处理器开始运行起来,等待Request的到来。

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

  Volley先读缓存然后,没有cache hit的话再从网络上获取,所以先启动CacheDispatcher,然后启动NetworkDispatcher。不过在启动处理器前先调用stop()函数清除掉以前RequestQueue里的过期的Dispatcher(Dispatcher都是继承Thread)。以防影响性能。Volley启动一个CacheDispatcher和4个NetworkDispatcher,之所以这样设计,个人人为是主要考虑到网络图片的下载,所以利用多个NetworkDispatcher来处理网络请求。然后看一下stop()函数。

   public void stop() {
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        for (int i = 0; i < mDispatchers.length; i++) {
            if (mDispatchers[i] != null) {
                mDispatchers[i].quit();
            }
        }
    }

   调用Dispatcher的quit()函数来结束线程。以NetworkDispatcher.quit()为例:

 public void quit() {
        mQuit = true;
        interrupt();
    }

  函数将mQuit变量置为true。为什么要这样做,因为在networkdispatcher线程中的中断异常处理中,判断mQuit的值,如果真,则退出循环,结束线程。否则continue,继续从Queue中去取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;
            }

  接下来,看下RequestQueue的add函数。

 public Request add(Request request) {
        // Tag the request as belonging to this queue and add it to the set of current requests.
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there‘s already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                // There is already a request in flight. Queue up.
                Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // Insert ‘null‘ queue for this cacheKey, indicating there is now a request in
                // flight.
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

  首先将Request加入到mCurrentRequests中,因为存在多个线程竞争的问题,在这个代码块上进行了同步。然后request.setSequence().为当前Request分配一个序列号,为什么这样做,因为我们下面要将Request放到NetworkQueue中或者CacheQueue中,这两个队列都是PriorityBlockingQueue,里面的元素是根据自定义的权重来排序的。PriorityBlockingQueue里的元素须实现Comparable接口,来看下我们这里的Requeset的实现:

 @Override
    public int compareTo(Request<T> other) {
        Priority left = this.getPriority();
        Priority right = other.getPriority();

        // High-priority requests are "lesser" so they are sorted to the front.
        // Equal priorities are sorted by sequence number to provide FIFO ordering.
        return left == right ?
                this.mSequence - other.mSequence :
                right.ordinal() - left.ordinal();
    }

  Request的策略是现根据每个Request的Priority来判断,如果两个Request的Priority相同,那么载根据两个Request的Sequence来进行判断队列里的先后顺序。

1 public enum Priority {
2         LOW,
3         NORMAL,
4         HIGH,
5         IMMEDIATE
6     }

给当前Request加上序列后,判断一下当前Request是否需要缓存,如果不需要则直接把Request加入到NetworkQueue队列里。如果需要缓存,取出Request的缓存键,从mWaitingRequests里看下有没有Request的缓存键.在RequestQueue中有四个队列。mCurrentRequests,mWaitingRequests,mCacheQueue,mNetworkQueue。每当一个请求到来时,先加入到mCurrentRequests,然后判断当前Request是否需要缓存,如果不用缓存的Request,则直接加入到mNetworkQueue队列中等待网络处理器(NetWorkDispatcher)去处理。如果需要缓存的话,根据Request获取相应的cacheKey,如果cacheKey不存在的话,说明这个需要缓存的Request是第一次请求。那么将cacheKey放入到mWaitingRequests队列里。(这里插播一下,mCurrentRequests存放的是所有交由RequestQueue处理的Request,mWaitingRequests里存放的是mCacheQueue里已经有相同url的Request,mWatiingRequests的出现就是为了避免不必要的网络数据获取),并将Request放入到mCacheQueue中以做处理。

1   // Insert ‘null‘ queue for this cacheKey, indicating there is now a request in
2                 // flight.
3                 mWaitingRequests.put(cacheKey, null);
4                 mCacheQueue.add(request);

如果cacheKey存在的话,说明已经有相同的Request正在处理(这里的cacheKey是通过getUrl()得到的,也就是创建Request时的url)。这时将此Request放入到mWaitingRequest队列中等待In-flight Request的处理结果。

add完然后看finish(Request req);

 1  void finish(Request request) {
 2         // Remove from the set of requests currently being processed.
 3         synchronized (mCurrentRequests) {
 4             mCurrentRequests.remove(request);
 5         }
 6
 7         if (request.shouldCache()) {
 8             synchronized (mWaitingRequests) {
 9                 String cacheKey = request.getCacheKey();
10                 Queue<Request> waitingRequests = mWaitingRequests.remove(cacheKey);
11                 if (waitingRequests != null) {
12                     if (VolleyLog.DEBUG) {
13                         VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
14                                 waitingRequests.size(), cacheKey);
15                     }
16                     // Process all queued up requests. They won‘t be considered as in flight, but
17                     // that‘s not a problem as the cache has been primed by ‘request‘.
18                     mCacheQueue.addAll(waitingRequests);
19                 }
20             }
21         }
22     }

首先先从mCurrentRequests集合中remove掉当前Request,然后在mWaitingRequests中去掉当前的Request.然后将此Request对应的mWaitingRequest中存储的Request放到mCacheQueue中等待处理(因为此时对应的url的网络数据已经加载到本地,所以这些mWaitingRequests里的Request被处理时直接从本地解析,不用耗时的网络获取一遍)。

RequestQueue类中还有一个CancelAll()函数,它的作用是根据指定的Request tag来删除响应的Request.

    public void cancelAll(RequestFilter filter) {
        synchronized (mCurrentRequests) {
            for (Request<?> request : mCurrentRequests) {
                if (filter.apply(request)) {
                    request.cancel();
                }
            }
        }
    }

    /**
     * Cancels all requests in this queue with the given tag. Tag must be non-null
     * and equality is by identity.
     */
    public void cancelAll(final Object tag) {
        if (tag == null) {
            throw new IllegalArgumentException("Cannot cancelAll with a null tag");
        }
        cancelAll(new RequestFilter() {
            @Override
            public boolean apply(Request<?> request) {
                return request.getTag() == tag;
            }
        });
    }

  

时间: 2024-08-05 15:15:59

Volley学习(RequestQueue分析)的相关文章

Volley学习(Volly分析)

记录下学习心得: Volley是android平台上的网络通讯库,能使网络通信更快,更简单,更便捷.Volly适合数据量不大但是通信频繁的场景. 先贴下Volley的架构图: Volley框架设计的主要几个类是Request(以及Volley提供的子类ImageRequest,StringRequest,JsonReques等),Volly,RequestQueue,CacheDispatcher,DiskBasedCache,NetworkDispatcher,BasicNetWork,Hur

volley学习笔记

前言 这个是第一次写源码分析的文章(仅仅是给自己做个也给自己两天对volley学习的一个交代吧).以前的老大经常强调一种代码阅读能力(如何通过源码的阅读了解框架.流程.及使用注意细节呢)网上查找了很多相关资料,如何培养源码阅读能力,但也是零零闪闪不全面.这可能与源码阅读设计到多方面,经验+知识面宽度等等.(这里不总结,个人修道还不深,求大神带飞),但是个人觉得思路还是一样的,源码阅读也罢新技术学习也罢.重点在于 核心概念+流程+关键点这里写上个人理解. 核心概念:每一个框架都有自己的核心概念(如

[Android]Volley源码分析(四)

上篇中有提到NetworkDispatcher是通过mNetwork(Network类型)来进行网络访问的,现在来看一下关于Network是如何进行网络访问的. Network部分的类图: Network有一个实现类BasicNetwork,它有一个mHttpStack的属性,实际的网络请求是由这个mHttpStack来进行的,看BasicNetwork的performRequest()方法, 1 @Override 2 public NetworkResponse performRequest

Volley源码分析

Volley源码分析 Volley简介 volley官方地址 在Google I/0 2013中发布了Volley.Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮. 这是Volley名称的由来:a burst or emission of many things or a large amount at once.Volley特别适合数据量不大但是通信频繁的场景. Github上面已经有大神做了镜像,使用Gradle更方便.Volley On Github Vo

[Android]Volley源码分析(二)Cache

Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源码思路往下,来看下Volley对Cache的处理逻辑. 我们回想一下昨天的简单代码,我们的入口是从构造一个Request队列开始的,而我们并不直接调用new来构造,而是将控制权反转给Volley这个静态工厂来构造. com.android.volley.toolbox.Volley: public static RequestQueue newRequestQueue(Context conte

[Android]Volley源码分析(肆)应用

通过前面的讲述,相信你已经对Volley的原理有了一定了解.本章将举一些我们能在应用中直接用到的例子,第一个例子是 NetworkImageView类,其实NetworkImageView顾名思义就是将异步的操作封装在了控件本身,这种设计可以充分保留控件的移植性和维护性.NetworkImageView通过调用setImageUrl来指定具体的url: public void setImageUrl(String url, ImageLoader imageLoader) { mUrl = ur

Volley学习(网摘)

Android网络通讯架构--Volley 1. Volley提供的功能简单来说,它提供了如下的便利功能: JSON,图像等的异步下载: 网络请求的排序(scheduling) 网络请求的优先级处理 缓存 多级别取消请求 和Activity和生命周期的联动(Activity结束时同时取消所有网络请求) 2.学习前的准备:下载volley源码打成jar包(我通过这个网址下载不下了,但是网络上很多好心人已经分享了源码) 下载地址: mirror of volley library from andr

[Android] Volley源码分析(一)体系结构

Volley:google出的一个用于异步处理的框架.由于本身的易用性和良好的api,使得它能得以广泛的应用.我还是一如既往从源码的方向上来把控它.我们先通过一段简单的代码来了解Volley RequestQueue queue = Volley.newRequestQueue(this); ImageRequest imagerequest = new ImageRequest(url, new Response.Listener<Bitmap>(){ @Override public vo

[Android]Volley源码分析(五)

前面几篇通过源码分析了Volley是怎样进行请求调度及请求是如何被实际执行的,这篇最后来看下请求结果是如何交付给请求者的(一般是Android的UI主线程). 类图: 请求结果的交付是通过ResponseDelivery接口完成的,它有一个实现类ExecutorDelivery, 主要有postResponse()与postError()两个方法,分别在请求成功或失败时将结果提交给请求发起者. 1. 首先,在NetworkDispatcher的run()方法中,当服务器返回响应并解析完后,会调用