Android网络通信Volley框架源代码浅析(二)
尊重原创 http://write.blog.csdn.net/postedit/25921795
在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列:本地队列和网络队列
/** The cache triage queue. */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<? >>(); /** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<? >>();
与之相应的分别有本地线程和网络线程。通过对RequestQueue源代码的分析,本地线程有一条。而网络线程默认有四条,我们能够对网络线程的个数进行设置,我们首先来学习一下本地线程:
(1) CacheDispatcher.java
public class CacheDispatcher extends Thread { private static final boolean DEBUG = VolleyLog.DEBUG; //本地队列,从RequestQueue中传递进来的
private final BlockingQueue<Request<?>> mCacheQueue; //网络请求队列。也是从RequestQueue中传递进来,当本地缓存没有命中时。须要把请求从本地队列增加网络队列
private final BlockingQueue<Request<?>> mNetworkQueue; //磁盘缓存对象
private final Cache mCache; //就是用于从子线程向Ui线程发送数据
private final ResponseDelivery mDelivery; /** Used for telling us to die. */
private volatile boolean mQuit = false; /**
* 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;
} /**
* 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); // 缓存初始化,将磁盘中的数据读入内存
mCache.initialize(); while (true) {
try { // 堵塞式从队列中取出请求
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take"); // 推断request是否被取消了(调用cancel方法)。假设取消了就不运行,再次到队列中取请求
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()) {
// 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;
}
continue;
}
}
}
}
(2) NetworkDispatcher.java
public class NetworkDispatcher extends Thread {
/** 网络队列 */
private final BlockingQueue<Request<? >> mQueue;
/** 用于Http请求,依据前面的学习,他事实上使用的是HttpURLConnection或者HttpClient. */
private final Network mNetwork;
/** 本地缓存,网络请求成功后。放入缓存. */
private final Cache mCache;
/** For posting responses and errors. */
private final ResponseDelivery mDelivery;
/** Used for telling us to die. */
private volatile boolean mQuit = false; /**
* Creates a new network dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param queue Queue of incoming requests for triage
* @param network Network interface to use for performing requests
* @param cache Cache interface to use for writing responses to cache
* @param delivery Delivery interface to use for posting responses
*/
public NetworkDispatcher(BlockingQueue<Request<? >> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
} /**
* 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();
} @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void addTrafficStatsTag(Request<?> request) {
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
} @Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<? > request;
while (true) {
try {
// 从队列中堵塞式取出一个请求.
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 (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
} addTrafficStatsTag(request); // 通过NetWork的perfromRequest方法放回一个NetworkResponse对象
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete"); // 假设这个返回结果已经发送到了ui线程。就将它finish
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
} // 将NetworkResponse 解析成Response.
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete"); // 假设须要缓存。那么将结果存入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
} // 标记为已经发送
request.markDelivered();
//将数据发送到Ui线程
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
} private void parseAndDeliverNetworkError(Request<? > request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
}
通过上面的代码,我们来总结一下一个请求的运行过程吧:
1、一个请求就是一个Request对象,首先将Request对象增加到RequestQueue中.
2、推断Request能否够缓存,假设能够。则增加到本地缓存队列,否则增加网络队列
3、本地线程不断监听本地队列是否有请求。假设有请求取出来
4、推断Request是否取消,假设取消,处理下一个请求
5、推断缓存是否命中,假设没有命中。将该请求增加网络队列
6、假设命中,可是过期,相同将该请求增加网络队列
7、假设命中。而且不用刷新,那么直接放回结果。不用增加网络队列
8、假设命中,而且须要刷新。那么放回结果,而且增加网络队列
9、相同4条网络线程也在不断监听网络队列是否有请求,一旦发现有请求,取出请求,推断是否取消,假设取消。那么取出下一个请求
10、假设没有取消,那么通过NetWork进行http请求,将请求结果封装成NetworkResponse,然后转换为Response
11、假设能够缓存,那么将数据写入缓存
12、通过Delivery将Response返回到ui线程
通过以上12步,完毕了一个完整的请求
研究了这么久。我们还没有研究Request和Response是什么呢,假设熟悉http请求的同学相信非常好理解。
Request就是一个http请求。Response就是http返回的内容,先看看Request这个类吧
Request是一个抽象类。我仅仅介绍比較重要的几个方法:
public abstract class Request<T> implements Comparable<Request<T>> {
//Http 请求方法 POST,GET
private final int mMethod; /** 请求URL*/
private final String mUrl;
//用于出错时的回调接口
private final Response.ErrorListener mErrorListener; /** 这个请求在队列中的顺序 */
private Integer mSequence; ... /** 是否可以缓存 */
private boolean mShouldCache = true; /** 是否已经取消了,网络线程和本地线程都会对此推断,假设取消了就不请求了 */
private boolean mCanceled = false; /** 请求策略,比方设置最大重试次数之类的*/
private RetryPolicy mRetryPolicy; /**
* Creates a new request with the given method (one of the values from {@link Method}),
* URL, and error listener. Note that the normal response listener is not provided here as
* delivery of responses is provided by subclasses, who have a better idea of how to deliver
* an already-parsed response.
*/
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy()); mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
} /**
* Sets the retry policy for this request.
*
* @return This Request object to allow for chaining.
*/
public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
mRetryPolicy = retryPolicy;
return this;
} ... /**
* 通过此方法取消一个请求
*/
public void cancel() {
mCanceled = true;
} /**
* 推断是否已经取消.
*/
public boolean isCanceled() {
return mCanceled;
} /**
* 获取请求头
* @throws AuthFailureError In the event of auth failure
*/
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
} /**
* Returns a Map of POST parameters to be used for this request, or null if
* a simple GET should be used. Can throw {@link AuthFailureError} as
* authentication may be required to provide these values.
*
* <p>Note that only one of getPostParams() and getPostBody() can return a non-null
* value.</p>
* @throws AuthFailureError In the event of auth failure
*
* @deprecated Use {@link #getParams()} instead.
*/
@Deprecated
protected Map<String, String> getPostParams() throws AuthFailureError {
return getParams();
} /**
* Returns a Map of parameters to be used for a POST or PUT request. Can throw
* {@link AuthFailureError} as authentication may be required to provide these values.
*
* <p>Note that you can directly override {@link #getBody()} for custom data.</p>
*
* @throws AuthFailureError in the event of auth failure
*/
protected Map<String, String> getParams() throws AuthFailureError {
return null;
} public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
} /**
* 设置是否能缓存
*
* @return This Request object to allow for chaining.
*/
public final Request<? > setShouldCache(boolean shouldCache) {
mShouldCache = shouldCache;
return this;
} /**
* 推断是否可以缓存
*/
public final boolean shouldCache() {
return mShouldCache;
} /**
* 这是个抽象方法,我们必须实现,用于将NetworkResponse 转化为Response
* @param response Response from the network
* @return The parsed response, or null in the case of an error
*/
abstract protected Response<T> parseNetworkResponse(NetworkResponse response); /**
* 这个我们也必须实现,用于将Response发送到ui线程
* @param response The parsed response returned by
* {@link #parseNetworkResponse(NetworkResponse)}
*/
abstract protected void deliverResponse(T response); } 以下继续看看Response这个类:
public class Response<T> { /** 成功的时候回调. */
public interface Listener<T> {
/** Called when a response is received. */
public void onResponse(T response);
} /** 失败的时候回调 */
public interface ErrorListener {
/**
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
public void onErrorResponse(VolleyError error);
} /** 成功的时候创建一个Response. */
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
return new Response<T>(result, cacheEntry);
} /**
* 失败的时候创建一个Response
*/
public static <T> Response<T> error(VolleyError error) {
return new Response<T>(error);
} /** Parsed response, or null in the case of error. */
public final T result; /**
* Returns whether this response is considered successful.
*/
public boolean isSuccess() {
return error == null;
} //私有的,我们无法调用
private Response(T result, Cache.Entry cacheEntry) {
this.result = result;
this.cacheEntry = cacheEntry;
this.error = null;
} private Response(VolleyError error) {
this.result = null;
this.cacheEntry = null;
this.error = error;
}
}
学习了上面两个类后,我们须要知道例如以下知识:
Volley中的不论什么请求都是继承Request的。如Volley提供的StringRequest,JsonArrayRequest,JsonObjectRequest
ImageRequest等等。而且要实现当中的两个方法
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
abstract protected void deliverResponse(T response);
T是泛型,StringRequest中T表示String,后期我将会简介这几种Request的使用,敬请大家期待。。
。
最后在介绍一个接口,就是ResponseDelivery.java
它的一个实现类是ExecutorDelivery.java
public class ExecutorDelivery implements ResponseDelivery {
/** 执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每一个任务将怎样执行的机制(包含线程使用的细节、调度等)分离开来的方法。在线程池中经经常使用到 */
private final Executor mResponsePoster; /**
* 传入一个Handler,事实上就是执行在主线的Handler。我想你应该明确为什么他可以从子线程
将数据传入ui线程了
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
//这里调用了handler的post方法
handler.post(command);
}
};
} /**
* Creates a new response delivery interface, mockable version
* for testing.
* @param executor For running delivery tasks
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
} @Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
} @Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
} /**
* A Runnable used for delivering network responses to a listener on the
* main thread.
*/
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable; public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
} @SuppressWarnings("unchecked")
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
} // Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
//在这里调用了deliverResponse
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
} // If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
} // If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
好了,今天就写到这里吧,大家有什么不明确的欢迎留言讨论....
Android网络通信Volley框架源代码浅析(二)的更多相关文章
- Android网络通信Volley框架源代码浅析(一)
尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天開始,我打算为大家呈现关于Volley框架的源代码分析的文章,Volley ...
- Android网络通信Volley框架源代码浅析(三)
尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析.相信大家对于Volley有了初步的认识,可是假设想更深入的理解 ...
- Volley框架源代码分析
Volley框架分析Github链接 Volley框架分析 Volley源代码解析 为了学习Volley的网络框架,我在AS中将Volley代码又一次撸了一遍,感觉这样的照抄代码也是一种挺好的学习方式 ...
- android图片缓存框架Android-Universal-Image-Loader(二)
http://blog.csdn.net/king_is_everyone/article/details/35595515 这篇打算直接告诉大家怎么用吧,其实这个也不是很难的框架,大致使用过程如下: ...
- Android 网络通信框架Volley简介(Google IO 2013)
1. 什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient( ...
- Android 网络通信框架Volley(一)
转自:http://blog.csdn.net/t12x3456/article/details/9221611 1. 什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫 ...
- [转]Android 网络通信框架Volley简介(Google IO 2013)
Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v= ...
- 【转】Android 网络通信框架Volley简介(Google IO 2013)
Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v= ...
- Android 网络通信框架Volley简介
1.1. Volley引入的背景在以前,我们可能面临如下很多麻烦的问题. 比如以前从网上下载图片的步骤可能是这样的流程: 在ListAdapter#getView()里开始图像的读取. 通过Async ...
随机推荐
- 从exp入手分析漏洞
分析poc和分析exp有一些不一样,因为exp是人为构造后的东西,它会执行一段自定的shellcode.结果是根本不会触发异常或者异常在离触发点十万八千里的地方.这样分析poc的技巧就用不上了(因为无 ...
- 提高eclipse使用效率(二)—— 提高Android开发效率的小技巧
XML文件的代码提示 adt中也有xml文件的代码提示,为了让提示来的更加猛烈,我们还要设置一下 打开eclipse - Window - Preferences,在右边的目录树中切换到XML - X ...
- USACO 4.2 Job Processing
Job ProcessingIOI'96 A factory is running a production line that requires two operations to be perfo ...
- STM32 串口通信
1. 中断说明 TXE(Tansmit Data Register empty interrupt) - 发送数据寄存器空,产生中断.当使能TXE后,只要Tx DR空了,就会产生中断.---写寄存器D ...
- 牛客练习赛3 E - 绝对半径2051
链接:https://www.nowcoder.net/acm/contest/13/E来源:牛客网 题目描述
- Oracle数据库DDL,DML,视图,PLSQL编程
动手敲~~~ --创建一个表空间--beijing create tablespace beijing datafile 'c:\beijing.dbf' size 100m autoextend o ...
- 【原创】MHA二次检测功能测试
MHA提供了很多扩展的功能,其中有一个参数是secondary_check_script,这个参数可以使我们自定义扩展多路由,多链路的二次检测功能.减少网络故障切换,降低脑裂的发生. 在虚拟机上做了如 ...
- sublime3176注册码破解汉化及常用插件
官方网站下载地址:https://www.sublimetext.com/3 破解软件下载地址:https://www.lanzous.com/i1a7zfi 破解软件下载地址备用:https://d ...
- BZOJ 2002: [Hnoi2010]Bounce 弹飞绵羊 lct 动态树 splay
http://www.lydsy.com/JudgeOnline/problem.php?id=2002 http://blog.csdn.net/frods/article/details/5224 ...
- 2017-2018-1 JAVA实验站 第八周作业
2017-2018-1 JAVA实验站 第八周作业 详情请见团队博客