Volley的Request的添加、移除和取消
来源:互联网 发布:淘宝三无产品如何投诉 编辑:程序博客网 时间:2024/05/19 04:52
本文系转载,转载于Volley源码分析 1,原文作者写的不错,很赞!
我们先来看一下Volley的架构图:
可以看出,Volley至少工作在3个线程当中,其中
蓝色部分为主线程:主要的工作是将请求按照优先级的顺序添加到cache的队列当中,当发出去的请求的得到相应的时候,在主线程将结果进行分发。
绿色部分为cache线程:如果cache hit,那么直接将cache中的数据进行解析,并传递给主线程,如果miss,那么则交给NetworkDispatcher进行处理。
黄色部分则为网络线程:与cache线程不同,cache只有一个线程在工作,而网络线程则可以有多个同时工作,进行网络请求,解析结果,写入cache,最终也是响应结果交给主线程。
我们以一段代码为例,跟踪代码的流程,对比上面的框架图,看看Volley到底是如何工作的。
RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this); JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html", null, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { // TODO Auto-generated method stub Log.i(TAG, response.toString()); }},new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // TODO Auto-generated method stub Log.i(TAG, error.getMessage()); }}); mQueue.add(jsonObjectRequest);
首先,我们新建了一个RequestQueue
的实例,当然在实际使用中,我们应该使用Application
的Context
作为Volley
的Context
。
然后,我们新建了一个JsonObjectRequest
,即一个Json
的请求,在其中的回调函数onResponse
中添加我们收到结果要做的事情,当然我们知道这部分代码是运行在主线程当中。
最后,将JsonObjectReques
的实例添加到RequestQueue
当中。
这就是Volley
使用的全部的代码,总体看来,我们所能看到的所有的请求都是进入RequestQueue
,然后就等待处理,得到onResponse
的响应,因此,我们可以跟随代码,从mQueue.add(jsonObjectRequest)
来分析。
- 任务的添加:
mQueue.add(jsonObjectRequest)
public <T> Request<T> add(Request<T> request) { // Tag the request as belonging to this queue and add it to the set of current requests. //将request与该任务队列相关联。 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. // 如果该日志不需要Cache的话,那么跳过cache的队列,直接进行网络请求 if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } // 如果程序运行到这里,说明需要缓存cache,那么进行的操作是,先检查当前的任务有没有在cache的运行当中, // 如果正在进行,或者说cache对应的cacheKey有Reqeust正在执行,那么则直接加入到cacheKey对应的队列当中即可。 // 如果需要cache,而且没有正在这行,则添加到等待队列和cache队列当中。 // Insert request into stage if there's already a request with the same cache key in flight. // 同步任务队列,根据该请求是否添加到RequestQueue的不同情况,分别处理 synchronized (mWaitingRequests) { //判断等待队列是否包含当前添加的任务 String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) { // There is already a request in flight. Queue up. // 该cacheKey对应的任务之前添加过,并且还没有处理完成。则取出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 { // Insert 'null' queue for this cacheKey, indicating there is now a request in // flight. // 与上面不同的是,当前的任务没有处理过,所以将任务添加到等待队列中,然后添加到cache的队列中。 mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request); } return request; } }
上面的代码就是RequestQueue
的add()
方法。我们可以画一下它的流程图
从中,可以看出,RequestQueue中add(Request request)
所做的工作为:
- 绑定
request
到此RequestQueue.this
- 将
request
添加到mCuurentRequest
的链表中 - 为
request
设置序列号,并打印Log
- 根据
request
是否需要cache
,如果不需要cache
,则直接将其放入mNetworkueue
当中。 - 如果
request
需要cache
,则检查该request
对应的cacheKey
(一般实际上使用URL
)是否已经在mWaitingRequests
列表中存在,如果已经存在,那么则更新cacheKey
所对应的列表,如果不存在,则将其放进mCacheQueue
中,再放入mNetworkQueue
中等待执行。 add(Request request)
方法执行完毕,返回。
此时我们也可以明白在RequestQueue
中的几个集合类的作用:
//该map的作用是用来缓存正在执行的需要Cache的Requestprivate final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();//所有的需要处理的Reqeust都会在这个集合当中private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();//mCacheQueue保存的是需要从Cache中获取的Requestprivate final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();//mNetworkQueue保存的是需要网络操作的Requestprivate final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();
对应的RequestQueue
的add
方法,我们再来看一下对应的finish(Request<?> request)
方法。
- 任务的结束:
finish(Request<?> request)
可以看出,RequestQueue
的finish(Request request)
主要做了以下的事情:
- 将
reqeust
从当前的任务列表mCurrentRequests
中删除 - 判断该
request
是否是需要cache
的请求,如果不是,则直接退出即可。 - 如果该
request
需要cache
,那么则删除所对应的cacheKey
,然后将等待的请求全部加入到mCacheQueue
即cache
的任务队列当中。其实finish()调用的并不是说任务要取消,而是说任务已经完成了,所以对应的也很容易理解了,首先该任务完成了,就应该从RequestQueue
中消失,如果不需要cache
,那么直接退出就可以啦,如果需要cache,那么就可以删除该cacheKey(URL)
对应的请求列表,因为这个时候网络任务应该已经完成,所以将剩下的任务加入到mCacheQueue
当中,让他们从cache
中获取就可以。)。
还有一点需要注意的是,该方法是的访问限定符是默认的,即包访问权限,并且在Volley
的源代码中,仅仅是Request.finish(String tag)
调用了该方法。
接下来我们进一步分析一下Request
中的finish(String tag)
方法,同时我们注意到,对于Reqeust
主要是一些标记变量和关于请求的内容,在逻辑上其他的相关的代码并不多。
/** * Notifies the request queue that this request has finished (successfully or with error). * * 通知请求队列,该请求已经完成,或者成功,或者存在错误。 * * <p>Also dumps all events from this request's event log; for debugging.</p> * <p>导出event log的所有的时间,用来调试。</p> * * 该方法可能来自CacheDisptacher,ResponseDelivery,NetworkDispatcher的调用 */ void finish(final String tag) { // 通知RequestQueue停止该任务 if (mRequestQueue != null) { mRequestQueue.finish(this); } //如果需要记录日志,则记录之。 if (MarkerLog.ENABLED) { //获取线程Id final long threadId = Thread.currentThread().getId(); //判断当前的Looper的线程是否为主线程,也就是判断当前的代码是否在主线程上运行。 if (Looper.myLooper() != Looper.getMainLooper()) { // If we finish marking off of the main thread, we need to // actually do it on the main thread to ensure correct ordering. // 如果我们是在其他的线程(非main线程)上取消任务,那么我们需要在主线程上来完成以保证正确的顺序。 // 这边的顺序没有特别看到,为什么要这样设计。 //获取主线程的handler Handler mainThread = new Handler(Looper.getMainLooper()); //分发取消请求的任务。 mainThread.post(new Runnable() { @Override public void run() { mEventLog.add(tag, threadId); mEventLog.finish(this.toString()); } }); return; } //这里就是主线程 mEventLog.add(tag, threadId); mEventLog.finish(this.toString()); } else { long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime; if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) { VolleyLog.d("%d ms: %s", requestTime, this.toString()); } } }
此处代码比较简单,Request
的finish
方法主要是调用了RequestQueue
的finish()
方法来标记完成任务。进而打印相关的Log信息,此处有一点不太明白的是,为什么一定要在主线程上打印Log?
任务的取消:cancelAll(final Object tag)
/** * Cancels all requests in this queue for which the given filter applies. * @param filter The filtering function to use */ 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; } }); }
这里面才是真正的取消任务,其主要的工作如下:
同步mCurrentRequests
,因为要遍历任务,所以要同步该集合。
依次判断Request
是否符合tag
或者RequestFilter
的要去,如果符合,则取消相应的任务。
而Request
的cancel
方法则更加简单,如下:
public void cancel() { mCanceled = true; }
现在看来,如果要取消某个Request
,Volley
不会主动的要求Network
或者Cache
的工作线程停止当前的正在执行的Request
,而是在执行过程中,如果发现Reqeust
的mCanceled
标记被设置,那么就不再进行下一步操作。
- Volley的Request的添加、移除和取消
- iOS通知的添加和移除
- jquery复选的添加和移除
- 状态的添加和移除。class
- listbox之间item的添加和移除
- oracle中数据表字段的添加和移除
- 通知的添加和移除通过block实现
- js外部样式和style属性的添加移除
- iOS通知观察者的添加和移除
- 实现View的添加和移除动画
- Volley定制自己的Request
- git - 移除文件以及取消对文件的跟踪
- ios 本地推送的添加和取消
- Volley框架请求取消的实现
- subView的添加与移除
- 对ListBox的添加移除操作
- Vuejs的添加与移除
- 字符串的添加/插入/移除
- android 独立编译ko包方法
- ASCII转十六进制
- [bzoj4550] 小奇的博弈
- Hive性能优化
- 修改_qmaster_named的方法
- Volley的Request的添加、移除和取消
- 矩阵的特征值和特征向量的雅克比算法C/C++实现
- JQuery随记
- SSL--用Tomcat服务器配置https双向认证过程实战
- Java 8 :: 方法引用操作符的介绍
- Python中的相关分析correlation analysis
- JS02-简介
- 排序-归并排序
- Pyhon3之部分内置函数使用