简介
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); 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);
先创建一个请求队列,然后构建一个请求对象,最后将请求对象添加到请求队列中,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) { stack = new HurlStack(); } else { 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() { 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); response.intermediate = true; mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(request); } catch (InterruptedException e) { } } }); } } 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"); 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.setRequestQueue(this); synchronized (mCurrentRequests) { mCurrentRequests.add(request); } request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue"); if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); 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 { 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) { 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")
那么我们需要取消该请求的时候就简单了:
RequestQueue.cancelAll("request1")
如此就取消了tag=request1的请求。
如此一来每个请求都需要设置不同的tag来确定唯一的请求标记。那么问题来了,我在整个应用退出时该如何取消所有的请求呢?不可能我每个请求都去调用一次cancleAll吧?此时只要调用cancelAll(RequestFilter filter)方法就可以轻而易举的取消所有request请求啦,代码如下:
requestQueue.cancelAll(new RequestQueue.RequestFilter() { @Override public boolean apply(Request<?> request) { return true; } });
解析:以上代码仅仅是修改了RequestFilter接口中apply方法的返回值永远为true而已。如此一来就会导致如下方法会全部遍历一次当前请求队列。
public void cancelAll(RequestFilter filter) { synchronized (mCurrentRequests) { for (Request<?> request : mCurrentRequests) { if (filter.apply(request)) { request.cancel(); } } } }
总结
这篇博客主要介绍了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错误重试机制的,以及本地缓存的策略和请求优先级的设置。