您的位置:首页 > 理论基础 > 计算机网络

4、Volley解析(二),源码的深入分析一,缓存线程和网络请求线程

2017-04-28 15:21 555 查看

前言

首先来看一下谷歌的官方流程图



从图中我们可以看出Volley工作的时候有三种线程,分别是主线程、请求线程、缓存线程,图中的流程大致如下:首先将请求放入缓存队列(队列中会根据优先级来排序),缓存线程将请求从缓存线程中取出,如果缓存存在,则将缓存的数据取出解析,传到主线程。如果不存在则将请求队列去执行网络请求。请求完成之后,再把结果发送给主线程。这里就是利用线程生产者-消费者模式

1. 源码的入口

分析某个开源项目的源码,首先要找到它的入口,然后再抽丝剥茧,逐步分析。这样思路会比较清晰。

//这个方法要做的工作是创建工作线程池的默认实例,并调用
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) {
}
//如果没有限定stack,由系统自己判断使用哪种请求方式
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
//adk版本在9或者以上,使用HttpURLConnection
//它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。
stack = new HurlStack();
} else {
// 在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}

Network network = new BasicNetwork(stack);

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
//
queue.start();

return queue;
}


今天这一篇博客,先不去研究用哪种请求方式,先介绍各个线程的工作流程。

创建好了HttpStack之后,接下来又创建了一个Network对象,它是用于根据传入的HttpStack对象来处理网络请求的,紧接着new出一个RequestQueue对象,并调用它的start()方法进行启动,然后将RequestQueue返回。

看到这里我们肯定会疑问,start()到底做了什么工作,会不会和线程有关呢?下面我们就来看一下。

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();
}
}


这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,也就是说当调用了satrt()之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。

我们首先来看一下CacheDispatcher

@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 {

//首先从mCacheQueue中取出一个任务,
// at least one is available.
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 {//刷新缓存
// 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;
}

continue;
}
}
}


到这里相信大家已经对CacheDispatcher有了足够的了解,下面我们就来看看NetworkDispatcher 主要职责是什么代码如下

@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}

try {
request.addMarker("network-queue-take");

// If the request was cancelled already, do not perform the
// network request.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
//统计流量
addTrafficStatsTag(request);

// Perform the network request.
//从网络解析请求,获得响应
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");

// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
//支持304重定向
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}

// Parse the response here on the worker thread.
//解析网络响应到本地
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");

// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
//将数据缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}

// Post the response back.
//将数据发送给主线程
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);
}
}
}

private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}


这样 CacheDispatcher 和 NetworkDispatcher就构成了生产者和消费者模式,当然充当生产者的不仅仅是CacheDispatcher 线程,还有主线程。其实CacheDispatcher 和 NetworkDispatcher大部分逻辑都是相同的,理解了一个另一个就不难理解了。今天这篇博客就到这里,下篇将为大家分析具体的网络请求是怎么样的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络 源码 线程