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 ...
随机推荐
- 转载:mysql数据同步redis
from: http://www.cnblogs.com/zhxilin/archive/2016/09/30/5923671.html 在服务端开发过程中,一般会使用MySQL等关系型数据库作为最终 ...
- cobbler之ks文件编辑
kickstart文件的组成部分: 命令段:用于配置系统 软件包:指定要安装的程序包及程序包组 %packages 标识 @Base:使用@指定包组 lftp:直接写程序包名 注 ...
- python-最好大学排名
# -*- coding: utf-8 -*-"""Created on Mon Apr 3 09:37:52 2017 @author: zuihaodaxuepaim ...
- MySQL 事务 是对数据进行操作,对结构没有影响,比如创建表、删除表,事务就不起作用
- 如何在java中发起http和https请求
一般调用外部接口会需要用到http和https请求. 一.发起http请求 1.写http请求方法 //处理http请求 requestUrl为请求地址 requestMethod请求方式,值为&qu ...
- SpringMVC TaskExecutor线程池
一.配置jdbc.properties添加: #------------ Task ------------ task.core_pool_size=5 task.max_pool_size=50 t ...
- shell脚本通过expect脚本实现自动输入密码(使用expect)
背景:在远程文件下载时,需要输入对方的服务器密码,shell不支持交互输入内容,可以用下面两种方式实现 一.在shell脚本中嵌入expect来实现密码输入 expect是一个自动交互功能的工具. ...
- impulse···········
impulse - Bing dictionary US['ɪm.pʌls]UK['ɪmpʌls] n.冲动:冲量:推动力:刺激 v.推动 网络脉冲:冲击 变形Plural Form:impulses ...
- sqlserver 几种datatime的区别
参考文章1 smalldatetime 占4位精确到分钟.时间从1900.1.1到2079.6.6datetime占8位精确到毫秒.时间从1753.1.1到9999.12.31 参考文章2 datet ...
- Spring boot starter pom的依赖关系说明
Spring Boot 通过starter依赖为项目的依赖管理提供帮助.starter依赖起始就是特殊的maven依赖,利用了传递依赖解析,把常用库聚合在一起,组成了几个为特定功能而定制的依赖. sp ...