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 ...
随机推荐
- JVM内存管理基础
JVM 虚拟机架构(图片来源: 浅析Java虚拟机结构与机制) JVM 内存区域 JVM会将Java进程所管理的内存划分为若干不同的数据区域. 这些区域有各自的用途.创建/销毁时间: (图片来源: ...
- Mongodb 折腾笔记
简介: Mongodb 是一个由 C++ 语言编写的基于分布式文件存储的数据库,是目前最像关系型数据库的非关系型数据库. 下载地址:https://fastdl.mongodb.org/linux/m ...
- AKKA集群中的分布式发布订阅
集群中的分布式发布订阅 如何向一个不知道在哪个节点上运行的actor发送消息呢? 如何向集群中的所有actor发送感兴趣的主题的消息? 这种模式提供了一个中介actor,akka.cluster.pu ...
- php 利用迭代器遍历文件夹
1.遍历文件夹 scandir 2.原生的迭代器Iterrate $scan_dir = "txtDir"; //下面会遍历txtDir 下面所有字文件夹中的文件哦 $dir_it ...
- 导出 java.io.IOException: 权限不够
项目原先都是开发使用root账号登陆服务器,人肉部署. 今天改成了自动部署,部署之后发现导出用不了了,查看服务器日志提示: 查看项目启动用户是app,推断是app用户的权限不够,导致导出无法在服务器创 ...
- xpath定位--绝对与相对的定位
xpath定位--绝对与相对的定位: xpath定位即为xml路径语言,它是一种用来确定xml文档中某部分位置的语言,xpath基于xml的树状结构,提供在数据结构中找寻节点的能力 xpath的相对定 ...
- 45. Jump Game II (Array; Two-Pointers,Greedy)
Given an array of non-negative integers, you are initially positioned at the first index of the arra ...
- Inside Triangle
Inside Triangle https://hihocoder.com/contest/hiho225/problem/1 时间限制:10000ms 单点时限:1000ms 内存限制:256MB ...
- TZOJ 1545 Hurdles of 110m(01背包dp)
描述 In the year 2008, the 29th Olympic Games will be held in Beijing. This will signify the prosperit ...
- 2-Qt关闭子窗口时执行特定代码
https://blog.csdn.net/naibozhuan3744/article/details/82689434 本文主要总结在关闭qt的QWidget子窗口瞬间,执行特定代码.由于主窗口关 ...