201709021工作日记--Volley源码解读(四)
接着volley源码(三)继续,本来是准备写在(三)后面的,但是博客园太垃圾了,写了半天居然没保存上,要不是公司这个博客还没被限制登陆,鬼才用这个。。。真是垃圾
继续解读RequestQueue的源码,Volley 的入口是创建一个 RequestQueue 队列,然后开启一个缓存线程和一组网络线程,等待用户 add 新的 request。那我们现在看一下 add 方法里面,RequestQueue 做了哪些事情。
/**
* 添加一个请求到这个消息队列中去
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
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.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.
// 如果此前有相同的请求还没有返回结果的,就将此请求加入mWaiting队列中
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<>();
}
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;
}
}
下面是add()方法的流程图

通过源码和流程图就可以很好理解add()方法的作用了,下面我们来看一个更重要的方法start(),这个方法是承上启下的方法,增加完队列请求后需要处理这些请求消息,将这些消息分发到缓存消息调度线程和网络调度线程中去处理。代码如下:
/**
* 开启队列中的线程调度者
* Starts the dispatchers in this queue.
*/
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();
}
}
开启了缓存消息调度线程和网络消息调度线程之后,我们来看看CacheDispatcher这个类干了些啥:
/**
* 内部就是阻塞队列的使用。
* 提供用于在请求队列上执行缓存分流的线程。
* Provides a thread for performing cache triage on a queue of requests.
*
* 将请求添加到指定的缓存队列中去,
* 任何可交付的响应通过{@link ResponseDelivery}发回给呼叫者,
* 缓存未命中,或者处于刷新队列中的,则指派给网络分发器
*
* Requests added to the specified cache queue are resolved from cache.
* Any deliverable response is posted back to the caller via a
* {@link ResponseDelivery}. Cache misses and responses that require
* refresh are enqueued on the specified network queue for processing
* by a {@link NetworkDispatcher}.
*/
public class CacheDispatcher extends Thread { private static final boolean DEBUG = VolleyLog.DEBUG; /**
* The queue of requests coming in for triage.
* 请求队列的分流
* */
private final BlockingQueue<Request<?>> mCacheQueue; /**
* 将要被分发到网络请求队列中的请求
* The queue of requests going out to the network.
* */
private final BlockingQueue<Request<?>> mNetworkQueue; /** The cache to read from. */
private final Cache mCache; /**
* 发布响应消息
* For posting responses.
* */
private final ResponseDelivery mDelivery; /**
* 阻塞队列取消等待的标志信息
* Used for telling us to die.
* */
private volatile boolean mQuit = false; /**
* 创建一个缓存触发的调度线程,需要调用start()方法触发开启这个线程
* Creates a new cache triage dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param cacheQueue Queue of incoming requests for triage
* @param networkQueue Queue to post requests that require network to
* @param cache Cache interface to use for resolution
* @param delivery Delivery interface to use for posting responses
*/
public CacheDispatcher(
BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
} /**
* 强制停止线程的请求操作。因为这个是阻塞消息队列,防止有消息一直占用资源而无法退出
* 设置标志位并且利用消息中断机制interrupt().
* 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();
} @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
// 从消息缓存的阻塞消息队列中获取到的请求消息,并且标记了下
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take"); // If the request has been canceled, don't bother dispatching it.
//如果请求取消了,继续后面的步骤
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.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;
}
}
}
}
}
首先需要提的是这两个阻塞队列的使用,详细请看前面几篇阻塞队列,防止了线程长时间占用资源而不释放锁的情况,可以设置超时停止线程的标志位private volatile boolean mQuit = false;并且捕获异常InterruptedException()。
其次,从缓存队列取出请求,判断是否请求是否被取消了,如果没有则判断该请求是否有缓存的响应,如果有缓存的响应并且没有过期则对缓存响应进行解析并回调给主线程;如果没有缓存的响应,则将请求加入网络调度线程。
下面是这个类的主要流程图:

啊
啊
啊
201709021工作日记--Volley源码解读(四)的更多相关文章
- 201709021工作日记--Volley源码详解(五)
学习完了CacheDispatcher这个类,下面我们看下NetworkDispatcher这个类的具体细节,先上代码: /** * 提供一个线程执行网络调度的请求分发 * Provides a th ...
- 201709011工作日记--Volley源码详解(二)
1.Cache接口和DiskBasedCache实现类 首先,DiskBasedCache类是Cache接口的实现类,因此我们需要先把Cache接口中的方法搞明白. 首先分析下Cache接口中的东西, ...
- 20170908工作日记--Volley源码详解
Volley没有jar包,需要从官网上下载源码自己编译出来,或者做成相关moudle引入项目中.我们先从最简单的使用方法入手进行分析: //创建一个网络请求队列 RequestQueue reques ...
- 201709011工作日记--Volley源码详解(三)
1. RequestQueue类 我们使用 Volley 的时候创建一个 request 然后把它丢到 RequestQueue 中就可以了.那么来看 RequestQueue 的构造方法,含有四个参 ...
- 20170906工作日记--volley源码的相关方法细节学习
1. 在StringRequest类中的75行--new String();使用方法 /** * 工作线程将会调用这个方法 * @param response Response from the ne ...
- Bert系列 源码解读 四 篇章
Bert系列(一)——demo运行 Bert系列(二)——模型主体源码解读 Bert系列(三)——源码解读之Pre-trainBert系列(四)——源码解读之Fine-tune 转载自: https: ...
- 温故而知新 Volley源码解读与思考
相比新的网络请求框架Volley真的很落后,一无是处吗,要知道Volley是由google官方推出的,虽然推出的时间很久了,但是其中依然有值得学习的地方. 从命名我们就能看出一些端倪,volley中 ...
- Python Web Flask源码解读(四)——全局变量
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
- Volley源码分析(四)NetWork与ResponseDelivery工作原理
这篇文章主要分析网络请求和结果交付的过程. NetWork工作原理 之前已经说到通过mNetWork.performRequest()方法来得到NetResponse,看一下该方法具体的执行流程,pe ...
随机推荐
- Swift - JPush极光推送的使用3(根据Alias别名,给某个指定用户发推送)(转)
一.别名(alias)介绍 (1)我们可以给每一个安装了应用程序的用户,取不同别名来标识(比如可以使用用户账号的 userid 来作为别名). (2)以后给某个特定用户推送消息时,就可以用此别名来指定 ...
- 移动cup
intel处理器M和U H结尾的有什么具体区别 笔记本CPU 酷睿i 系列U M H详解: U 低压版(低电压-性能消减)最低主频:1.7-1.9GHZ M 准电压(笔记本上的电压标准)最低主频:2. ...
- C#实现支持单点登录的一个存储用户信息的类
网上有很多介绍单点登录的文章,但多为架构设计以及概念性文章,而本文将介绍单点登录的具体具体实现 利用哈希表,作为保存登录用户的队列 private static Hashtable m_ ...
- List<?>和List<T>的区别
是java泛型的两种用法:List<T>是泛型方法,List<?>是限制通配符 List<T>一般有两种用途:1.定义一个通用的泛型方法.伪代码: public i ...
- select语法图
- Java的反射和代理以及注解
最近接触到java的反射和代理(接触的有点迟了...),还是有必要总结下 1. Java的反射 有的时候我们需要在程序运行的时候获取类.方法等信息用于动态运行,这个时候反射就能够帮我们找到类.方法.成 ...
- 关于number...的精度问题
一 当数字的精度被定为number(3,2)时, 这时他能输入的数字整数部分只能是3-2=1位, 小数位如果不够会用0补齐, 超出的四舍五入保留3位小数. SQL> insert into t_ ...
- C++ 静态数据成员和静态成员函数
一 静态数据成员: 1.静态数据成员的定义. 静态数据成员实际上是类域中的全局变量.所以,静态数据成员的定义(初始化)不应该被放在头文件中,因为这样做会引起重复定义这样的错误.即使加上#ifndef ...
- RedisUtil工具类
转载:http://blog.csdn.net/liuxiao723846/article/details/50401406 1.使用了jedis客户端,对redis进行了封装,包括: 1)使用了re ...
- Ubuntu下多个版本OpenCV管理(Multiple Opencv version)
背景: 最近,在Nvidia的GPU嵌入式开发板Jetson TX1(简称TX1)上移植深度学习目标检测算法YOLO.在TX1上安装了官方提供的opencv版本——OpenCV4Tegra(OpenC ...