再回首 -- Volley源码解析
Posted 巨头之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了再回首 -- Volley源码解析相关的知识,希望对你有一定的参考价值。
前言
前面解析了Retrofit和OkHttp这两个框架的源码,提到网络框架,肯定少不了Volley这位老前辈,想曾经的项目用的可都是Volley,每个框架都有其适用的场景,只是随着时间的更迭,逐渐被替代。 那这次顺便也解析下Volley框架的源码,这里做个笔记
本篇的Volley版本基于 1.2.0-SNAPSHOT
1.首先看下Volley的基本使用
//第1步.创建RequestQueue(请求队列)
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
//第2步.创建所需的Request(请求),这里是创建StringRequest
StringRequest mStringRequest =
new StringRequest(Request.Method.GET, "http://www.baidu.com",
new Response.Listener<String>()
@Override
public void onResponse(String response)
Log.i("wangshu", response);
, new Response.ErrorListener()
@Override
public void onErrorResponse(VolleyError error)
Log.e("wangshu", error.getMessage(), error);
);
//第3步.将请求添加进请求队列中
mQueue.add(mStringRequest);
2.从Volley的基本使用API入手其源码:
a. 首先看下创建RequestQueue的代码,由Volley.newRequestQueue开始
public class Volley
public static RequestQueue newRequestQueue(Context context)
//调用下面标注1的newRequestQueue
return newRequestQueue(context, (BaseHttpStack) null);
//标注1
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack)
BasicNetwork network;
if (stack == null)
if (Build.VERSION.SDK_INT >= 9)
//android版本大于2.3,则创建基于HttpUrlConnection的HurlStack
network = new BasicNetwork(new HurlStack());
else
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
// At some point in the future we'll move our minSdkVersion past Froyo and can
// delete this fallback (along with all Apache HTTP code).
String userAgent = "volley/0";
try
String packageName = context.getPackageName();
PackageInfo info =
context.getPackageManager().getPackageInfo(packageName, /* flags= */ 0);
userAgent = packageName + "/" + info.versionCode;
catch (NameNotFoundException e)
//Android版本小于2.3,则创建基于HttpClient的HttpClientStack
network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
else
//stack != null的情况
network = new BasicNetwork(stack);
//调用下面标注2的newRequestQueue
return newRequestQueue(context, network);
//标注2
private static RequestQueue newRequestQueue(Context context, Network network)
final Context appContext = context.getApplicationContext();
// Use a lazy supplier for the cache directory so that newRequestQueue() can be called on
// main thread without causing strict mode violation.
DiskBasedCache.FileSupplier cacheSupplier =
new DiskBasedCache.FileSupplier()
private File cacheDir = null;
@Override
public File get()
if (cacheDir == null)
cacheDir = new File(appContext.getCacheDir(), DEFAULT_CACHE_DIR);
return cacheDir;
;
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheSupplier), network);
queue.start();
return queue;
结合代码和注释,Volley.newRequestQueue主要做了两件事
- 判断Android的系统版本是否大于2.3; Android版本大于或等于2.3,则创建基于HttpUrlConnection的HurlStack。 Android版本小于2.3,则创建基于HttpClient的HttpClientStack
- 创建请求队列RequestQueue,并调用其start函数,最后返回RequestQueue
接着进去RequestQueue的start 函数
public class RequestQueue
public void start()
//将当前正在执行的线程全部中断
stop();
//创建缓存线程并且启动它
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// mDispatchers数组默认动态初始化长度为4,所以网络执行线程默认创建4条
for (int i = 0; i < mDispatchers.length; i++)
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
在RequestQueue的start 函数中,初始化一个缓存调度线程(CacheDispatcher)和4个( 默认情况下 ) 网络调度线程(NetworkDispatcher),所以Volley默认情况下会在后台开启5个线程。线程都初始化之后,分别调用其start方法开启线程
b. 接着看下第2步创建的StringRequest类,看下StringRequest两个核心的函数
public class StringRequest extends Request<String>
//deliverResponse主要负责回调响应数据
@Override
protected void deliverResponse(String response)
Response.Listener<String> listener;
synchronized (mLock)
listener = mListener;
if (listener != null)
listener.onResponse(response);
//负责解析响应数据
@Override
@SuppressWarnings("DefaultCharset")
protected Response<String> parseNetworkResponse(NetworkResponse response)
String parsed;
try
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
catch (UnsupportedEncodingException e)
// Since minSdkVersion = 8, we can't call
// new String(response.data, Charset.defaultCharset())
// So suppress the warning instead.
parsed = new String(response.data);
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
可以看到StringRequest类的代码很简洁,deliverResponse函数负责将响应结果进行回调,parseNetworkResponse函数解析响应数据为Response实例,这里先记住deliverResponse和parseNetworkResponse这两个函数,后面会再碰到这个函数
c.将请求添加进请求队列中
public class RequestQueue
public <T> Request<T> add(Request<T> request)
request.setRequestQueue(this);
synchronized (mCurrentRequests)
mCurrentRequests.add(request);
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
//发送request生命周期事件
sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);
/**
* 如果这个request不支持缓存,那将直接添加到网络请求队列中,并返回这个request
* request.shouldCache() 默认为true,即开启缓存
*/
if (!request.shouldCache())
mNetworkQueue.add(request);
return request;
//request支持缓存,则添加到缓存队列中
mCacheQueue.add(request);
return request;
在add函数中主要通过request.shouldCache()判断是否支持缓存,不支持缓存则将request添加进mNetworkQueue(网络请求队列),支持缓存的话则将request添加到mCacheQueue(缓存队列); request.shouldCache( )默认情况下为true,即开启缓存
在上面的代码中,可以了解到:
- 默认情况下,是将请求添加到缓存队列中
- Volley中主要维护了了1个缓存调度线程(CacheDispatcher),和4个(默认情况下) 网络调度线程(NetworkDispatcher)
请求添加到缓存队列,我们就从缓存调度线程(CacheDispatcher)入手
3.CacheDispatcher的工作流程
CacheDispatcher继承自Thread,那核心自然是run函数.
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();
while (true)
try
processRequest();
catch (InterruptedException e)
// We may have been interrupted because it was time to quit.
if (mQuit)
Thread.currentThread().interrupt();
return;
VolleyLog.e(
"Ignoring spurious interrupt of CacheDispatcher thread; "
+ "use quit() to terminate it");
在run函数中开启了死循环,不断执行processRequest函数
private void processRequest() throws InterruptedException
//从缓存队列中取出请求
final Request<?> request = mCacheQueue.take();
processRequest(request);
@VisibleForTesting
void processRequest(final Request<?> request) throws InterruptedException
request.addMarker("cache-queue-take");
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED);
try
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled())
request.finish("cache-discard-canceled");
return;
// 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.
//返回true则表示之前有执行过该请求,属于重复请求
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request))
mNetworkQueue.put(request);
return;
// If it is completely expired, just send it to the network.
//true表示 缓存已到有效期
if (entry.isExpired())
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
//返回true则表示之前有执行过该请求,属于重复请求
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request))
mNetworkQueue.put(request);
return;
// 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");
/**
* 判断缓存的数据是否解析成功
* JsonObjectRequest的parseNetworkResponse函数中,解析失败的话会对Response类的error变量设置值
* response.isSuccess()则是判断error变量是否为null
*/
if (!response.isSuccess()) //缓存数据解析失败的情况
request.addMarker("cache-parsing-failed");
mCache.invalidate(request.getCacheKey(), true);
request.setCacheEntry(null);
//返回true则表示之前有执行过该请求,属于重复请求
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request))
mNetworkQueue.put(request);
return;
//缓存是否需要更新
if (!entry.refreshNeeded()) //不需要更新缓存的情况
// Completely unexpired cache hit. Just deliver the response.
//mDelivery的默认实现类是ExecutorDelivery,在RequestQueue类的构造函数中赋值的
//在ExecutorDelivery的postResponse函数中将子线程切换到主线程,并回调给Response.Listener的onResponse()方法
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;
//返回true则表示之前有执行过该请求,属于重复请求
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request))
// 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)
// Restore the interrupted status
Thread.currentThread().interrupt();
);
else //重复请求的情况
// request has been added to list of waiting requests
// to receive the network response from the first request once it returns.
//将数据回调给主线程 (Response.Listener的onResponse()方法 )
mDelivery.postResponse(request, response);
finally
request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
processRequest函数中大致做了如下几个判断:
- processRequest函数从缓存队列中取出请求,然后判断该请求是否被取消?
- 如果取消,则直接中止该函数;
- 如果没有取消,则判断该请求是否有对应的缓存?
- 如果没有缓存,则判断该请求是否已执行过,执行过则直接中止该函数,未执行过则添加到网络请求队列并中止该函数
- 如果有缓存,则判断该缓存是否过期?
- 如果已过期,则判断该请求是否已执行过,执行过则直接中止该函数,未执行过则添加到网络请求队列并中止该函数
- 如果未过期,则解析缓存的数据,接着判断缓存数据是否解析成功?
- 如果解析失败,则设置缓存无效,并判断该请求是否已执行过,执行过则直接中止该函数,未执行过则添加到网络请求队列并中止该函数
- 如果解析成功,接着判断该缓存是否需要更新?
- 如果不需要更新,则将第4步中缓存数据解析的结果回调给主线程
- 如果需要更新,则判断该请求是否已执行过?
- 如果该请求已执行过,将第4步中缓存数据解析的结果回调给主线程
- 如果该请求未执行过,将第4步中缓存数据解析的结果回调给主线程,并将请求添加到网络请求队列中
processRequest函数中调用request.parseNetworkResponse()解析缓存的数据, request是从缓存队列中取出的,所以这个request就是一开始API中调用mQueue.add() 添加到缓存队列的StringRequest,而StringRequest类的parseNetworkResponse函数在上面已经提到,是用于将响应数据解析为Response实例的
4.NetworkDispatcher的工作流程
NetworkDispatcher同样继承自Thread,run函数同样是开启了死循环,不断执行processRequest函数
public class NetworkDispatcher extends Thread
private void processRequest() throws InterruptedException
//从网络队列中取出请求
Request<?> request = mQueue.take();
processRequest(request);
@VisibleForTesting
void processRequest(Request<?> request)
long startTimeMs = SystemClock.elapsedRealtime();
request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
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");
request.notifyListenerResponseNotUsable();
return;
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 && 该请求已经请求过,则取消该次请求
//在下面的代码中request.markDelivered()会将该请求设置为已请求状态
if (networkResponse.notModified && request.hasHadResponseDelivered())
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
// 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.
requestVolley框架源码分析