上一篇我们介绍了Volley
的基本使用,这篇我们来看看它的实现,直接看下面这段代码:
RequestQueue rq = Volley.newRequestQueue(this);
StringRequest sr = new StringRequest(URL_GET,
new Response.Listener() {
@Override
public void onResponse(String response) {
Log.e("LHC", String.format("%s:%s", "volley_get_success:", response));
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("LHC", String.format("%s:%s", "volley_get_error:", error.getMessage()));
}
});
rq.add(sr);
上面这段代码是一个StringRequest
的GET
实现,比较简单。我们现在来具体分析下,首先看第一行代码:
RequestQueue rq = Volley.newRequestQueue(this);
用来生成了RequestQueue
对象,我们进入Volley.newRequestQueue(this)
进入源码来看看其内的实现:
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
这个方法很简单,只是实现了方法newRequestQueue
的重载;那么在进入方法有两个参数的newRequestQueue
中看看。
public static RequestQueue newRequestQueue(Context context, HttpStack stack){
return newRequestQueue(context, stack, -1);
}
这里的第二个参数HttpStack
用于网络默认可为NULL
。它也是实现了方法newRequestQueue
的重载,我们继续进入:
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
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) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1) {
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}else{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
queue.start();
return queue;
}
这个方法的第三个参数maxDiskCacheBytes
表示磁盘缓存的最大值(单位为字节),默认值为-1。看看其中这段代码:
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
当stack
为空,Build.VERSION.SDK_INT
大于9,通过HurlStack
为stack
赋值;
当stack
为空,Build.VERSION.SDK_INT
小于9,通过HttpClientStack
为stack
赋值;
这个为什么要进行这个处理呢,我们进入这两个类中看看:
public class HurlStack implements HttpStack {
......
@Override
public HttpResponse performRequest(Request> request, Map additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap map = new HashMap();
......
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
......
return response;
}
}
public class HttpClientStack implements HttpStack {
protected final HttpClient mClient;
public HttpClientStack(HttpClient client) {
mClient = client;
}
......
@Override
public HttpResponse performRequest(Request> request, Map additionalHeaders)
throws IOException, AuthFailureError {
......
return mClient.execute(httpRequest);
}
}
从上面代码可以看出,HurlStack
和HttpClientStack
都是实现HttpStack
接口,并在方法performRequest
中完成了网络请求并返回HttpResponse
对象;不同的在于HurlStack
是使用HttpURLConnection
进行网络通讯的,而HttpClientStack
是使用HttpClient
进行网络通讯的。
那么为什么要根据Build.VERSION.SDK_INT
是否大于9来进行不同的网络请求实例呢?
HttpClient
拥有众多的API,而且实现比较稳定,bug数量也很少;但是我们很难在不破坏兼容性的情况下对它进行升级和扩展。要注意的是在版本5.1
之后废止了其相关API。
HttpURLConnection
是一种多用途、轻量极的HTTP客户端,API提供的比较简单,但是我们可以更加容易地去使用和扩展它。
我们继续看newRequestQueue
方法中的这段代码:
RequestQueue queue;
if (maxDiskCacheBytes <= -1) {
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}else{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
queue.start();
}
如果没有设置磁盘缓存值,那么会默认设置磁盘缓存最大值为5M;
DiskBasedCache
类,就是将要缓存的文件缓存到磁盘上指定目录的一种缓存实现。这块的缓存是在内存中分配的,虽然提高了请求的执行速度,但是不适合进行大数据的传输,如果进行大数据的下载,很容易早成分配的这块缓存溢出。
这段代码生成了RequestQueue
对象并进行启动。最后将RequestQueue
对象返回,这样就完成了RequestQueue rq = Volley.newRequestQueue(this);
的实现。
我们继续看newRequestQueue
方法中的这段代码:
Network network = new BasicNetwork(stack);
很简单就是生成了一个Network
对象,具体来看看BasicNetwork
中代码实现:
public class BasicNetwork implements Network {
......
@Override
public NetworkResponse performRequest(Request> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
......
}
......
}
这个类就是具体的来实现网络请求的,并返回了NetworkResponse
对象。
来看看queue.start()
这句代码中的内部实现,代码:
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();
}
}
首先调用stop()
方法将正在运行的调度程序全部停止。然后生成了一个CacheDispatcher
对象,也就是缓存线程,并启动;最后在for
循环中直接生成了mDispatchers.length
个NetworkDispatcher
对相关,也就是网络线程,并启动。这样就直接启动了5个线程,在后台一直运行着,等待着网络请求的到来。其实mDispatchers.length
值为4,看代码:
......
/** The network dispatchers. */
private NetworkDispatcher[] mDispatchers;
......
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
......
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
......
/** Number of network request dispatcher threads to start. */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
......
所以,网络调度数组的大小也就是默认网络线程池的大小。
我们继续进入CacheDispatcher
中,看看缓存调度线程的内部实现:
public class CacheDispatcher extends Thread {
......
@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();
Request> request;
while (true) {
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
try {
// Take a request from the queue.
request = mCacheQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
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.
final Request> finalRequest = request;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(finalRequest);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
}
}
}
}
在这个线程的Run
方法中,有个while(true)
的死循环,它会再后台一直运行,直到有任务进来。通过Cache.Entry entry = mCache.get(request.getCacheKey());
来取出缓存实体值,当为NULL
时,将请求放入网络队列中;当entry.isExpired()
为true
时,也就是数据完全过期了,我们需要将其放入网络队列中;否则我们直接使用缓存当中的数据,不需要进行网络请求。在通过entry.refreshNeeded()
来判定是否需要来更新缓存数据。
进入NetworkDispatcher
中,看看网络调度线程的内部实现:
public class NetworkDispatcher extends Thread {
......
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request> request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
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.
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);
}
}
}
......
}
这个线程的Run方法中,有个while(true)的死循环,它会再后台一直运行,直到有任务进来。通过NetworkResponse networkResponse = mNetwork.performRequest(request);
来执行网络请求并得到NetworkResponse
对象;然后进行解析生成Response
对象,在通过request.shouldCache() && response.cacheEntry != null
来判定是否添加缓存。
完成RequestQueue
生成后,构建具体的Request
对象,将此对象加入RequestQueue
中,这样就完成了网络请求操作。来看看rq.add(sr);
中的代码实现:
public Request add(Request 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.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue> 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;
}
}
通过request.shouldCache()
来判定用户是否需要缓存,如果不需要的话,直接将我们生成的Request
对象加入到mNetworkQueue
网络队列中;如果需要则加入到mCacheQueue
缓存队列中。默认情况下是需要缓存的,除非你通过request.setsetShouldCache(false)
设置为不需要缓存。
上一篇:Volley
的基本使用
参考:
- https://blog.csdn.net/guolin_blog/article/details/17656437
- https://www.jianshu.com/p/b23c7952b30f