在 Android 开发编程的过程中,无可避免地须要涉及到网络编程,这就须要咱们开发者比较熟练地掌握网络编程。哈哈,今天就先从解析 Volley 开始吧!java
Volley 是在 2013 年的 Googel I/O 大会上面推出的一个 HTTP 库,它能够帮助 Android 应用更加方便地执行网络请求。它既能够访问网络取得数据,同时还能够访问网络取得图片。git
使用 Volley 的流程是,建立一个 RequestQueue
(请求队列)对象,而后把 Request
(请求)提交给它。github
// 代码[1]
final TextView textView = (TextView) MainActivity.this.findViewById(R.id.tv);
//[1.0]建立 RequestQueue 的实例
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this); // 1
String url = "http://gank.io/api/data/Android/10/1";
//[2.0]构造一个 request(请求)
StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
textView.setText("Response is: " + response.toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
textView.setText("Error is happenning");
}
});
//[3.0]把 request 添加进请求队列 RequestQueue 里面
requestQueue.add(request);复制代码
上面代码逻辑主要是经过构造一个 StringRequest
的实例,而后把这个实例添加进请求队列 RequestQueue
里面。咱们来查看注释 1 的 Volley.newRequestQueue(Context)
方法的源码:编程
// 源码[2]
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); // 1
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) { // 2
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); // 3
queue.start(); // 4
return queue;
}
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}复制代码
从上面的源码能够看出,newRequestQueue(Context)
是 newRequestQueue(Context, HttpStack)
的重载方法,下面咱们主要来看 newRequestQueue(Context, HttpStack)
方法,在注释 1 处经过 new File(File, String)
初始化构建一个 cacheDir
的缓存(这是一个名词),在注释 3 处,new DiskBasedCache(cacheDir)
为这个缓存分配了 5M 的存储空间;
回到注释 2, 当 SDK 的版本大于或等于 9 的时候,也就是 Android 的版本号大于或等于 2.3,则建立基于 HttpURLConnection
的 HurlStack
,不然就建立基于执行请求的 HttpClient
的 HttpClientStack
,而后在注释 3 处,经过 new RequestQueue(Cache, Network)
建立请求队列,咱们来查看下 new RequestQueue(Cache, Network)
的源码:api
// 源码[3]
...
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
...
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
...复制代码
构造方法 new RequestQueue(Cache, Network)
为网络请求分配了 4 条线程进行请求。对于 RequestQueue
这个类,主要的做用是做为一个线程池用于调度队列中的请求:当调用 add(Request)
将会把传进的请求(Request)在缓存队列(cache)或者网络队列(network)中解析,而后传递回主线程,也就是 代码[1] 里面的回调函数 onResponse(String)
和 onErrorResponse(VolleyError)
拿到回调的请求内容;
在 代码[1] 的注释 4 处,调用了 add(Request)
方法:缓存
// 源码[4]
public <T> Request<T> add(Request<T> request) {
request.setRequestQueue(this);
//同步代码块,保证 mCurrentRequests.add(request) 在一个进程里面的全部线程里面,有且只能在
//一个线程里面执行
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
//若是不能够存储,就把请求(request)添加进网络调度队列里面
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
//若是能够存储:
//同步代码块
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
//若是以前有相同的请求而且尚未返回结果的,就把此请求加入到 mWaitingRequests 里面
if (mWaitingRequests.containsKey(cacheKey)) {
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
//若是没有请求在进行中,从新初始化 Queue<Request<?>> 的实例
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
//若是没有的话,就把请求添加到 mCacheQueue 队列里面
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
}复制代码
从上面的 add(Request)
方法的源码能够看出,主要的逻辑是:若是请求(request)不能够缓存,就直接添加进缓存队列,不然添加进缓存队列; 当拿到 mNetworkQueue
和 mCacheQueue
之后,就把请求返回而且调用 start()
方法(如 源码[2] 的注释 4),当查看 start()
方法的源码时候:网络
// 源码[5]
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(BlockingQueue<Request<?>>, BlockingQueue<Request<?>>, Cache, ResponseDelivery)
的实例并经过 mCacheDispatcher.start()
开启缓存调度线程,和建立 NetworkDispatcher(BlockingQueue<Request<?>>,Network, Cache,ResponseDelivery)
的实例并经过 networkDispatcher.start()
开启网络调度线程。并发
网络调度线程 NetworkDispatcher
是一个继承于 Thread
的线程,经过查看其任务方法 run()
的源码:app
// 源码[6]
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
try {
// 从网络队列中取出请求
request = mQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 若是请求被取消
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 把网络请求到的实体缓存到 源码[2] 的注释 1 处的本地缓存文件里面
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
request.markDelivered();
// 回调请求到的响应给主线程
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
...
} catch (Exception e) {
...
}
}
}复制代码
网络调度的主要逻辑是,先判断网络请求有没有被取消,若是没有被取消,就经过 mNetwork.performRequest(request)
执行请求网络响应,拿到响应之后,先缓存在本地,而后再回调给主线程。框架
缓存调度线程 CacheDispatcher
的源码以下所示:
@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");
//若是请求被取消
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 {
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
...
}
}
});
}
} catch (InterruptedException e) {
...
}
}
}复制代码
一样地,若是请求没有被取消,就开始在取回本地的缓存,当本地的缓存不存在、丢失或者已通过期,就把请求添加到网络请求队列,当命中本地缓存,就把缓存的响应回调给主线程;
为了发送一个请求,你只须要构造一个请求并经过 add()
方法添加到 RequestQueue
中。一旦添加了这个请求,它会经过队列,经过一系列的调度,而后获得原始的响应数据并返回。
当执行 add()
方法时,Volley触发执行一个缓存处理线程以及一系列网络处理线程。当添加一个请求到队列中,它将被缓存线程所捕获并触发: 若是这个请求能够被缓存处理,那么会在缓存线程中执行响应数据的解析并返回到主线程。若是请求不能被缓存所处理,它会被放到网络队列中。网络线程池中的第一个可用的网络线程会从队列中获取到这个请求并执行HTTP操做,解析工做线程的响应数据,把数据写到缓存中并把解析以后的数据返回到主线程。
一个请求的生命周期
到此,Volley 库的分析就到先到此暂时结束了。此次,笔者主要是就当一个请求添加进请求队列以后,到其返回响应的过程进行了分析,了解到了 Volley 是如何把一个请求进行调度,而后获得响应并返回的。哈哈,感谢你的阅读...
源码下载 TestVolley