简析Android网络请求Volley框架的工作原理

来源:互联网 发布:nba火箭vs尼克斯数据 编辑:程序博客网 时间:2024/05/16 13:52

简介

Volley是Google开发的一个用于网络请求的开源库,它使得Android开发者更加简单,快速的请求网络数据。

Volley有如下优点: 
1. 自动调度网络请求 
2. 多并发请求 
3. 本地Cache自动缓存网络请求结果 
4. 支持设置请求优先级 
5. 支持取消单个请求或者取消所有请求 
6. 易于定制请求(比如:自定义重试机制,自定义Request请求等) 
7. 提供完善的Log打印跟踪工具

Volley工作原理

这里借用Google的一张Volley原理图来简单解释下Volley的工作原理。 
这里写图片描述

Volley请求处理是一个异步的过程:

1.在主线程中按照请求的优先级把Request添加到本地缓存队列CacheQueue中, 
2.缓存分发器CacheDispatcher轮询本地是否已经缓存了这次请求的结果, 
3.如果命中,则从缓存中读取数据并且解析。解析完的结果被分发到主线程中。 
4.如果没有命中,则将这次请求添加到网络请求队列NetworkQueue中, 
5.网络分发器NetworkDispatcher处理网络请求,获取请求结果并解析同时把结果写入缓存。解析完的结果被分发到主线程中。

有上图可知,Volley的整个过程有三类线程在工作:

  • 主线程:所有的请求结果都会被分发到主线程。
  • 缓存线程:专门有一个线程用于读取本地缓存。
  • 网络线程:Volley默认开启4个线程去处理网络请求。

开发者无需关注子线程的内部实现流程,只需要在主线程中将一个Request请求投放到请求队列RequestQueue,然后在主线程中实现一个接口回调拿到请求结果Response即可。也就是Volley在主线程中投放一个任务,异步的在主线程中获得任务的结果。如此,Volley就很方便,简单的使用在网络请求中,简化开发者搭建一个网络请求异步任务框架。

Volley初始化以后就创建了5个后台线程在处理请求。只要你没做处理,这5个线程一直在后台跑。为了节省资源,在同一个App中最好使用同一个单例Volley RequestQueue队列来处理所有请求,以免创建过多线程浪费资源。还有在推出这个应用时,应该调用 RequestQueue#stop方法来干掉所有Volley线程。如此才是使用Volley最优雅的方式

在Volley工作原理中涉及到如下几个主要类:

  • Request:所有网络请求的基本类,该类是个泛型的抽象类,用户可以根据需求定制指定的请求类型,比如:Volley就实现了StringRequest请求类,该类型请求返回的结果是String类型。
  • RequestQueue:带有调度功能的请求队列。所有的Requset请求都会通过该类的add()方法加入到相应的队列中。
  • CacheDispatcher:缓存调度分发器。该类继承自线程,主要工作是轮询缓存队列,取出队列中的请求,读取本地缓存。然后由ResponseDelivery将结果发送到主线程。
  • NetworkDispatcher:网络调度分发器。该类继承自线程,主要工作是轮询网络请求队列,取出队列中的请求,执行网络操作。然后由ResponseDelivery将结果发送到主线程。
  • Response:响应请求结果解析类。
  • ExecutorDelivery:响应结果执行分发器,用于将请求结果分发到主线程中。
  • DiskBasedCache:本地缓存管理类,用于缓存网络请求结果到指定的目录。
  • BasicNetwork:执行网络请求类,用于执行所有的网络请求。
  • DefaultRetryPolicy:Volley默认的出错重试机制类。用于指定网络重试次数,超时时间等设置。
  • HttpHeaderParser:HTTP头部解析类,用于解析HTTP请求结果头部信息,来判断缓存文件是否有效或者过期。

从源码角度理解Volley工作原理

Volley最基本的使用代码如下:

//创建请求队列 RequestQueue mQueue = Volley.newRequestQueue(context); //构建一个Request请求 StringRequest request = new StringRequest(url, new Response.Listener<String>() {     @Override     public void onResponse(String response) {     } }, new Response.ErrorListener() {     @Override     public void onErrorResponse(VolleyError error) {     } }); //将请求添加到队列中 mQueue.add(request);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

先创建一个请求队列,然后构建一个请求对象,最后将请求对象添加到请求队列中,Volley即可帮我们完成一次网络请求,并且把结果回调过来。开发者需要写的代码很少,只需要调用Volley类中的静态方法newRequestQueue()就可以得到RequestQueue对象。我们来看看Volley#newRequestQueue()方法如何实现的?

Volley类

public class Volley {    /**默认缓存目录 */    private static final String DEFAULT_CACHE_DIR = "volley";    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        //创建默认缓存文件        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);        String userAgent = "volley/0";        try {            String packageName = context.getPackageName();            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);            userAgent = packageName + "/" + info.versionCode;        } catch (NameNotFoundException e) {        }        if (stack == null) {            if (Build.VERSION.SDK_INT >= 9) {//API>=9使用HttpURLConnection访问网络                stack = new HurlStack();            } else {//API<9时使用HttpClient访问网络                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }        //创建网络访问类        Network network = new BasicNetwork(stack);        //创建请求队列        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);        //开始执行队列中的任务        queue.start();        return queue;    }    /**静态方法创建请求队列*/    public static RequestQueue newRequestQueue(Context context) {        return newRequestQueue(context, null);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

代码第8行: 创建一个默认的缓存文件目录,该路径在应用的私有目录data/data/your_package/cache/volley/ 下。

代码第18行: 判断stack 等于null,则去创建一个HttpStack 对象,如果API>=9,则使用HurlStack类来访问网络,而HurlStack类的实现是基于HttpURLConnection来实现的。如果API<9,则使用HttpClientStack来来访问网络,而HttpClientStack类的实现是基于HttpClient来实现的。由此可知,Android到底用哪个API来访问网络Volley已经给出标准答案了。API>=9使用HttpURLConnection,反之使用HttpClient访问网络。所以Volley也可以定制不同的网络API。

代码28行:创建一个网络请求队列RequestQueue 对象,然后调用start()方法启动执行队列中的任务。那么RequestQueue#start()方法到底做了什么?接下来分析下RequestQueue类的实现。

RequestQueue

public class RequestQueue {    /** 用于标识Request的编号. */    private AtomicInteger mSequenceGenerator = new AtomicInteger();    /**保存添加到RequestQueue队列中相同key的请求*/    private final Map<String, Queue<Request<?>>> mWaitingRequests =            new HashMap<String, Queue<Request<?>>>();    /**保存当前所有添加到RequestQueue队列中的请求*/    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();    /** 带有优先级的缓存请求队列. */    private final PriorityBlockingQueue<Request<?>> mCacheQueue =        new PriorityBlockingQueue<Request<?>>();    /** 带有优先级的网络请求队列. */    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =        new PriorityBlockingQueue<Request<?>>();    /** 默认开启4个线程处理网络请求 */    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;    /** 本地缓存,用于保存网络请求结果 */    private final Cache mCache;    /** 用于执行网络请求. */    private final Network mNetwork;    /** 请求结果分发器. */    private final ResponseDelivery mDelivery;    /** 网络处理请求分发器. */    private NetworkDispatcher[] mDispatchers;    /** 本地缓存处理请求分发器. */    private CacheDispatcher mCacheDispatcher;    public RequestQueue(Cache cache, Network network, int threadPoolSize,            ResponseDelivery delivery) {        mCache = cache;        mNetwork = network;        mDispatchers = new NetworkDispatcher[threadPoolSize];        mDelivery = delivery;    }    public RequestQueue(Cache cache, Network network, int threadPoolSize) {        this(cache, network, threadPoolSize,                new ExecutorDelivery(new Handler(Looper.getMainLooper())));    }    public RequestQueue(Cache cache, Network network) {        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);    }    /**     * 启动队列中的任务调度     */    public void start() {        stop();  //停止当前所有任务.        // 创建缓存任务调度器,并且启动它        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);        mCacheDispatcher.start();        // 创建默认个数的网络任务调度器,并且启动它.        for (int i = 0; i < mDispatchers.length; i++) {            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,                    mCache, mDelivery);            mDispatchers[i] = networkDispatcher;            networkDispatcher.start();        }    }    /**     * 停止缓存和网络调度     */    public void stop() {        if (mCacheDispatcher != null) {            mCacheDispatcher.quit();        }        for (int i = 0; i < mDispatchers.length; i++) {            if (mDispatchers[i] != null) {                mDispatchers[i].quit();            }        }    }    /**     * 获取队列编号.     */    public int getSequenceNumber() {        return mSequenceGenerator.incrementAndGet();    }    /**     * 获取本地缓存.     */    public Cache getCache() {        return mCache;    }    /**     完成一次请求,当该请求被执行结束或者该请求被取消时调用该方法     */    <T> void finish(Request<T> request) {        // 从当前队列中移除该请求,标志着该请求得到执行。        synchronized (mCurrentRequests) {            mCurrentRequests.remove(request);        }        synchronized (mFinishedListeners) {          for (RequestFinishedListener<T> listener : mFinishedListeners) {            listener.onRequestFinished(request);          }        }        //如果该请求允许有缓存,则将等待队列中的所有的请求任务全部添加到缓存队列中继续执行。        if (request.shouldCache()) {            synchronized (mWaitingRequests) {                String cacheKey = request.getCacheKey();                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);                if (waitingRequests != null) {                    if (VolleyLog.DEBUG) {                        VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",                                waitingRequests.size(), cacheKey);                    }                    //处理等待队列中所有的请求.                    mCacheQueue.addAll(waitingRequests);                }            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133

RequestQueue#start方法 
有上面的代码可知:start方法中创建了一个CacheDispatcher缓存调度处理器和4个NetworkDispatcher网络调度处理器,而他们都是继承自Thread线程的,所以这里创建了1个缓存线程和4个网络线程来处理Request请求。相当于此处启动了5个线程来处理请求,这就是为什么Volley框架支持多并发请求了。那么我们看看它们都做了些什么??

CacheDispatcher类

public class CacheDispatcher extends Thread {    /** 缓存阻塞队列. */    private final BlockingQueue<Request<?>> mCacheQueue;    /** 网络阻塞队列. */    private final BlockingQueue<Request<?>> mNetworkQueue;    /** 本地缓存. */    private final Cache mCache;    /** 结果分发器. */    private final ResponseDelivery mDelivery;    /** 标记当前线程是否死亡. */    private volatile boolean mQuit = false;    public CacheDispatcher(            BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,            Cache cache, ResponseDelivery delivery) {        mCacheQueue = cacheQueue;        mNetworkQueue = networkQueue;        mCache = cache;        mDelivery = delivery;    }    /**退出当前线程*/    public void quit() {        mQuit = true;        interrupt();    }    @Override    public void run() {//设置该线程的优先级为后台线程                           Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        //初始化本地缓存.        mCache.initialize();        //死循环        while (true) {            try {                //从阻塞的缓存队列中取出一个请求.                final Request<?> request = mCacheQueue.take();                request.addMarker("cache-queue-take");                // 如果该请求被取消,就不处理该请求                if (request.isCanceled()) {                    request.finish("cache-discard-canceled");                    continue;//继续下次循环,等待下一个请求                }                //试图从本地缓存中读取本次请求结果.                Cache.Entry entry = mCache.get(request.getCacheKey());                //本地没有命中则将该请求投放到网络请求队列中处理                if (entry == null) {                    request.addMarker("cache-miss");                    mNetworkQueue.put(request);                    continue;//结束本次循环                }                // 本地命中,但是过期了,也需再次将请求投放到网络请求队列中                if (entry.isExpired()) {                    request.addMarker("cache-hit-expired");                    request.setCacheEntry(entry);                    mNetworkQueue.put(request);                    continue;//结束本次循环                }                // 本地缓存命中该请求以后解析该请求                request.addMarker("cache-hit");                Response<?> response = request.parseNetworkResponse(                        new NetworkResponse(entry.data, entry.responseHeaders));                request.addMarker("cache-hit-parsed");                if (!entry.refreshNeeded()) {                    // 本地缓存命中且没有过期,则将解析的结果发送到主线程中.                    mDelivery.postResponse(request, response);                } else {                    // 本地缓存命中,但需要刷新,重新将这次请求投放到网络请求队列中                    request.addMarker("cache-hit-refresh-needed");                    request.setCacheEntry(entry);                    // Mark the response as intermediate.                    response.intermediate = true;                    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) {                // 此处用于退出当前线程,当该线程发生中断异常时执行.                if (mQuit) {                    return;                }                continue;            }        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

解析:CacheDispatcher类继承自Thread,实现了run方法,在run方法中写了一个while(true)死循环,用于一直读取缓存队列中的请求任务。因为缓存队列mCacheQueue是一个阻塞队列,所以只有队列不为空时while循环才会取出下一个新的请求任务执行,否则while循环一直阻塞直到有新任务添加进来。

run方法实现的逻辑是:先从本地缓存中去读本次请求,如果该请求命中本地缓存且缓存未过期,则解析结果并且有分发器ResponseDelivery 将结果发送到主线程中。如果本地没有命中或者命中的请求过期了,则将该请求投放到网络请求队列中,由NetworkDispatcher来处理网络请求。

更多详细细节请看以上代码注释,写的很详细了。

这里写图片描述

NetworkDispatcher类

NetworkDispatcher类实现和CacheDispatcher类类似,这里直接贴出run方法的实现:

public class NetworkDispatcher extends Thread {..................    @Override    public void run() {    //设置线程优先级为后天线程        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);        while (true) {            long startTimeMs = SystemClock.elapsedRealtime();            Request<?> request;            try {                // 从队列中取出一个请求任务.                request = mQueue.take();            } catch (InterruptedException e) {                // 当线程发生中断异常时,判断该线程是否死亡?如果死亡则结束循环,否则跳出本次循环继续等待下一个请求任务到来。                if (mQuit) {                    return;                }                continue;            }            try {                request.addMarker("network-queue-take");                // 该Request请求如果被取消,则跳出本次循环,结束本地请求处理                // network request.                if (request.isCanceled()) {                    request.finish("network-discard-cancelled");                    continue;                }                addTrafficStatsTag(request);                // 执行网络请求.                NetworkResponse networkResponse = mNetwork.performRequest(request);                request.addMarker("network-http-complete");                // 相同的结果不发送第二次                if (networkResponse.notModified && request.hasHadResponseDelivered()) {                    request.finish("not-modified");                    continue;                }                // 在工作线程中解析网络结果.                Response<?> response = request.parseNetworkResponse(networkResponse);                request.addMarker("network-parse-complete");                // 将结果保存到缓存中.                if (request.shouldCache() && response.cacheEntry != null) {                    mCache.put(request.getCacheKey(), response.cacheEntry);                    request.addMarker("network-cache-written");                }                request.markDelivered();                //将结果发送到主线程中                mDelivery.postResponse(request, response);            } catch (VolleyError volleyError) {                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);                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);            }        }    }  ...........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

解析:NetworkDispatcher 类同样继承自Thread,实现了其run方法。在该方法中写了一个while(true)死循环,用于读取网络队列中的Request请求任务,同样由于网络队列也是一个阻塞队列,所以当队列不为空就取出一个Request任务,然后将该任务值执行网络请求,并且解析请求结果,在得到网络请求结果以后首先将结果保存到本地缓存,然看结果将由分发器ResponseDelivery发送到主线程中。

到此,RequestQueue#start方法分析结束,总结起来如下:Volley会创建一个RequestQueue对象,该对象会创建一个Cache对象用于保存请求结果,创建一个带有优先级以及阻塞的缓存队列mCacheQueue用于保存用户添加的请求,创建一个CacheDispatcher线程调度器来轮询缓存队列mCacheQueue执行请求任务。创建了4个带有优先级和阻塞的网络队列mNetWorkQueue用于保存没有命中本地缓存的请求,匹配的也创建了4个NetworkDispatcher线程调度器来轮询mNetWorkQueue队列执行请求任务。

RequestQueue#add()

........ /**添加一个请求到带有分发器的队列中*/    public <T> Request<T> add(Request<T> request) {        //Request请求和请求队列关联        request.setRequestQueue(this);        synchronized (mCurrentRequests) {            //Request请求添加到当前请求队列中            mCurrentRequests.add(request);        }        // 给该请求设置一个顺序编号.        request.setSequence(getSequenceNumber());        request.addMarker("add-to-queue");        // 如果该请求不需要缓存,则将Request请求直接添加至网络请求队列中.        if (!request.shouldCache()) {            mNetworkQueue.add(request);            return request;        }        // 以下代码处理缓存请求等待队列.        synchronized (mWaitingRequests) {            String cacheKey = request.getCacheKey();            //如果等待队列中已经存在该请求的key,则说明此时有一个相同的请求正在被处理,因此将该request放在等待队列中,等待前一个request处理完之后在finish方法中处理。            if (mWaitingRequests.containsKey(cacheKey)) {                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 {                //如果等待队列中没有改请求的key,则将之添加到等待队列,注意:添加到等待队列中的value 是一个null,目的用于标记该请求在等待队列中且处于正在被处理的状态。                mWaitingRequests.put(cacheKey, null);                //添加都缓存队列中让该请求得到相应的执行。                mCacheQueue.add(request);            }            return request;        }    }........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

代码第13行:给当前请求设置一个编号,后面将用于设置请求的优先级,这里暂且不详细讲解。 
代码第17-20行:判断当前请求是否允许本地缓存,如果不允许,则直接将本次请求添加到网络请求队列中。否则添加到缓存请求队列中。 
代码26-35行:判断请求等待队列中是否包含本次请求的key,如果包含,则说明有相同的请求正在被执行,此时将该请求放入到等待队列中,等待上一个请求被执行完成以后再来处理此次的请求,见方法 finish()。 
代码38-39行:表示请求等待队列中并不包含本次请求的key,则先将本次请求在等待队列中置空,置空的目的是告诉别人该请求正在被执行,如果有其他相同的请求来时,请先等待我这次请求执行结束。然后将本次请求投放到缓存队列中,让CacheDispatcher调度器执行本次请求。

RequestQueue#finish完成一次请求时会调用该方法。

该方法的调用,标志着一次请求的结束,结束一次请求包括:

  • 一个请求被完整的处理,得到请求的结果。
  • 一个请求在处理的过程中被取消了。
<T> void finish(Request<T> request) {        // 从当前队列中移除该请求        synchronized (mCurrentRequests) {            mCurrentRequests.remove(request);        }        synchronized (mFinishedListeners) {        //请求结束的监听回调          for (RequestFinishedListener<T> listener : mFinishedListeners) {            listener.onRequestFinished(request);          }        }        if (request.shouldCache()) {            synchronized (mWaitingRequests) {                String cacheKey = request.getCacheKey();                //从等待队列中移除该请求                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);                if (waitingRequests != null) {                   //将等待队列中所有相同的请求一次性添加到缓存队列中,让CacheDispatcher线程处理。此处不敢苟同,既然是相同的请求,为啥要全部放入队列中?放一个就好了嘛!!                    mCacheQueue.addAll(waitingRequests);                }            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这段代码的解释在注释里写的很清楚了。

取消请求

我们都知道,Volley有个优势就是可以取消指定的Tag标记请求或者取消所有请求,那么Volley是怎么做到的呢?还是从源码中找答案吧。

RequestQ ueue#cancleAll()

    /**     *定义的过滤请求接口,用于构建取消某个请求或者所有请求     */    public interface RequestFilter {        public boolean apply(Request<?> request);    }    /**     根据给定的过滤条件取消队列中所有的请求     */    public void cancelAll(RequestFilter filter) {        synchronized (mCurrentRequests) {        //遍历当前请求队列中所有的请求,找到匹配的请求然后取消该请求。            for (Request<?> request : mCurrentRequests) {                if (filter.apply(request)) {                    request.cancel();                }            }        }    }    /**     根据给定的tag标签取消队列中所有的请求队列     */    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;            }        });    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

我们在添加请求的时候通常会给每个请求这是一个Tag,比如:

request.setTag("request1");
  • 1

那么我们需要取消该请求的时候就简单了:

RequestQueue.cancelAll("request1");
  • 1

如此就取消了tag=request1的请求。 
如此一来每个请求都需要设置不同的tag来确定唯一的请求标记。那么问题来了,我在整个应用退出时该如何取消所有的请求呢?不可能我每个请求都去调用一次cancleAll吧?此时只要调用cancelAll(RequestFilter filter)方法就可以轻而易举的取消所有request请求啦,代码如下:

 requestQueue.cancelAll(new RequestQueue.RequestFilter() {            @Override            public boolean apply(Request<?> request) {                return true;            }        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

解析:以上代码仅仅是修改了RequestFilter接口中apply方法的返回值永远为true而已。如此一来就会导致如下方法会全部遍历一次当前请求队列。

  public void cancelAll(RequestFilter filter) {        synchronized (mCurrentRequests) {        //遍历当前请求队列中所有的请求,找到匹配的请求然后取消该请求。            for (Request<?> request : mCurrentRequests) {                if (filter.apply(request)) {                    request.cancel();                }            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

总结

这篇博客主要介绍了Volley的整体工作机制,从整篇博客我们知道:

  • Volley默认创建1个cache Thread和4个network Thread来处理网络请求,当然你也可以创建更多的network Thread来处理更多的网络请求,正因为如此,Volley才支持多并发网络连接。
  • Volley创建1个本地缓存队列(cacheQueue)和1个网络请求队列(networkQueue)来保存所有网络请求,而随之对应的是一个cache Thread处理缓存队列,4个network thread处理网络请求队列,由于队列的实现都是带有优先级的阻塞队列,因此4个network thread是自动调度处理网络请求的。
  • Volley默认先将请求提交给cache Thread来处理,cache Thread会查找本地是否缓存了本次请求结果,如果缓存了且该结果未过期,则直接读取本地缓存结果,而无须再次请求网络。因此Volley默认自动缓存网络请求结果。
  • Volley支持取消某个或者所有的网络请求,一般在某个activity退出时调用Request#cancelAll()方法来取消所有网络请求以便出现内存泄漏。
  • Volley初始化以后就创建了5个后台线程在处理请求。只要你没做处理,这5个线程一直在后台跑。为了节省资源,在同一个App中最好使用同一个单例Volley RequestQueue队列来处理所有请求,以免创建过多线程浪费资源。还有在退出这个应用时,应该调用 RequestQueue#stop方法来干掉所有Volley线程。如此才是使用Volley最优雅的方式

后续博客会继续分析Volley是怎么实现RetryPolicy错误重试机制的,以及本地缓存的策略和请求优先级的设置。