Volley源碼閱讀
如何閱讀源碼?關(guān)于這個(gè)翔忽,前人有很多的方法玷禽。一般我分析這類(lèi)工程赫段,首先做的第一步是打開(kāi)這類(lèi)工程的開(kāi)發(fā)者官網(wǎng),官網(wǎng)上都會(huì)對(duì)這個(gè)工具庫(kù)進(jìn)行介紹矢赁。比如糯笙,它是什么?能解決什么問(wèn)題撩银?與其它同類(lèi)工具比較给涕,有什么優(yōu)點(diǎn)和劣勢(shì)(可能有些工程不會(huì)在官網(wǎng)上提到這個(gè))。
想要分析它额获,首先要知道開(kāi)發(fā)者是怎么開(kāi)發(fā)它的够庙,開(kāi)發(fā)過(guò)程中使用了什么技巧,軟件的架構(gòu)是什么樣的抄邀。還有就是教程耘眨,開(kāi)發(fā)者一般都給出了使用教程。從使用教程中境肾,我們知道程序的使用方式剔难,也就是程序的入口。還有就是有一些前輩已經(jīng)分析過(guò)了源碼奥喻,他們的博客能為我們解決不少困惑偶宫。
接下來(lái),我們開(kāi)始源碼分析环鲤。從教程中纯趋,我們知道,要想發(fā)起一個(gè)請(qǐng)求冷离,我們至少需要?jiǎng)?chuàng)建兩個(gè)對(duì)象:RequestQueue和Request吵冒。Request就是我們想要發(fā)起的請(qǐng)求,可以設(shè)置請(qǐng)求方式西剥、Header和Url痹栖。RequestQueue是一個(gè)執(zhí)行Request的對(duì)象。
創(chuàng)建一個(gè)RequestQueue有幾個(gè)簡(jiǎn)單的方式蔫耽,分別是:
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack A {@link BaseHttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack)
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
* @deprecated Use {@link #newRequestQueue(Context, BaseHttpStack)} instead to avoid depending
* on Apache HTTP. This method may be removed in a future release of Volley.
*/
@Deprecated
@SuppressWarnings("deprecation")
public static RequestQueue newRequestQueue(Context context, HttpStack stack)
private static RequestQueue newRequestQueue(Context context, Network network)
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context)
四個(gè)構(gòu)建方式,第一個(gè)構(gòu)建方式留夜,入?yún)⒂蠧ontext和BaseHttpStack,從注釋中匙铡,我們知道,Context是用于創(chuàng)建緩存地址碍粥,而B(niǎo)aseHTTPStack用于網(wǎng)路請(qǐng)求鳖眼,如果傳的是空的,則會(huì)使用默認(rèn)的嚼摩。然后钦讳,會(huì)返回一個(gè)已經(jīng)開(kāi)始運(yùn)行的RequestQueue實(shí)例(即矿瘦,創(chuàng)建了緩存線(xiàn)程和工作線(xiàn)程池,并啟動(dòng)了他們)愿卒。
第二個(gè)被廢棄了缚去,我們不仔細(xì)講了,可以看注釋琼开。
第三個(gè)比較簡(jiǎn)單易结,傳入了一個(gè)Context和Network,同樣Network也是用來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的。
第四個(gè)是我們常用的柜候,直接傳入Context搞动,用于創(chuàng)建緩存路徑。其實(shí)渣刷,從源碼中看鹦肿,它其實(shí)調(diào)用了第一個(gè)構(gòu)建方式,只不過(guò)第二個(gè)參數(shù)傳的是空辅柴。
那么我們來(lái)看看第一個(gè)和第三個(gè)構(gòu)建方式的源碼箩溃。
/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack A {@link BaseHttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {
BasicNetwork network;
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
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) {
}
network =
new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}
return newRequestQueue(context, network);
}
private static RequestQueue newRequestQueue(Context context, Network network) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
先看第三個(gè)構(gòu)建方式,因?yàn)槁凳叮谝粋€(gè)碾篡、第二個(gè)和第四個(gè)都間接的調(diào)用了第三個(gè)構(gòu)建方式。(其實(shí)正常的分析過(guò)程中筏餐,我是先看的第四個(gè)構(gòu)建方式开泽,然后再分析到了第一個(gè),最后是第三個(gè)魁瞪。嗯穆律,對(duì)的,這種方法叫做順藤摸瓜)
這個(gè)構(gòu)建方式是靜態(tài)私有的导俘,有兩個(gè)參數(shù)峦耘,分別是Context和Network。Context用于創(chuàng)建緩存路徑旅薄;Network用于實(shí)現(xiàn)網(wǎng)絡(luò)傳輸辅髓。函數(shù)的第一行,根據(jù)context獲取App的cacheDir和Volley默認(rèn)的相對(duì)DIR少梁,創(chuàng)建了一個(gè)文件cacheDir洛口。然后,根據(jù)這個(gè)cacheDir創(chuàng)建了一個(gè)DiskBasedCache凯沪,使用這個(gè)DiskBasedCache和傳進(jìn)來(lái)的network創(chuàng)建了一個(gè)RequestQueue第焰。調(diào)用RequestQueue的start()方法,啟動(dòng)它妨马。然后返回這個(gè)RequestQueue挺举。這個(gè)印證了杀赢,在教程中說(shuō)的,使用newRequestQueue方法湘纵,會(huì)創(chuàng)建一個(gè)RequestQueue并啟動(dòng)它脂崔。關(guān)鍵點(diǎn)是RequestQueue類(lèi),我們打開(kāi)源碼分析它瞻佛。
RequestQueue類(lèi)
這個(gè)類(lèi)的描述是:
/**
* A request dispatch queue with a thread pool of dispatchers.
*
* <p>Calling {@link #add(Request)} will enqueue the given Request for dispatch, resolving from
* either cache or network on a worker thread, and then delivering a parsed response on the main
* thread.
*/
簡(jiǎn)單說(shuō)脱篙,就是一個(gè)帶有派發(fā)者線(xiàn)程池的請(qǐng)求派發(fā)隊(duì)列。調(diào)用add()方法伤柄,將會(huì)入隊(duì)你傳入的請(qǐng)求绊困,分析是從緩存還是在工作線(xiàn)程的網(wǎng)絡(luò)請(qǐng)求中得到結(jié)果,然后在主線(xiàn)程中將的解析后的響應(yīng)結(jié)果傳送出來(lái)适刀。
分析一個(gè)具體的類(lèi)秤朗,看完類(lèi)的基本描述后,我首先會(huì)看看它的構(gòu)造函數(shù)笔喉。這個(gè)類(lèi)有三個(gè)構(gòu)造函數(shù),分別是:
/** Number of network request dispatcher threads to start. */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache interface for retrieving and storing responses. */
private final Cache mCache;
/** Network interface for performing requests. */
private final Network mNetwork;
/** Response delivery mechanism. */
private final ResponseDelivery mDelivery;
/** The network dispatchers. */
private final NetworkDispatcher[] mDispatchers;
/** The cache dispatcher. */
private CacheDispatcher mCacheDispatcher;
private final List<RequestFinishedListener> mFinishedListeners = new ArrayList<>();
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(
Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(
cache,
network,
threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
我們直接分析第一個(gè)構(gòu)造函數(shù)取视,因?yàn)楹竺鎯蓚€(gè)構(gòu)造函數(shù)均是調(diào)用第一個(gè)構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)功能的。
注釋說(shuō)了常挚,這個(gè)方法的作用是創(chuàng)建線(xiàn)程池作谭,當(dāng)然,此時(shí)還沒(méi)有啟動(dòng)奄毡,只有調(diào)用了start()方法折欠,才會(huì)開(kāi)啟。有四個(gè)參數(shù)吼过,分別是:cache锐秦、network、threadPoolSize和ResponseDelivery盗忱。cache用于持久化響應(yīng)結(jié)果到磁盤(pán)上酱床;network用于完成HTTP請(qǐng)求;threadPoolSize用于設(shè)置網(wǎng)絡(luò)派發(fā)者線(xiàn)程的數(shù)量趟佃;ResponseDelivery接口扇谣,用于發(fā)布響應(yīng)和錯(cuò)誤。在調(diào)用該方法時(shí)闲昭,其將你傳入的這些參數(shù)罐寨,賦值到私有變量中。并根據(jù)你傳的threadPoolSize汤纸,創(chuàng)建線(xiàn)程池衩茸。
第二個(gè)構(gòu)造函數(shù)有三個(gè)參數(shù)芹血,調(diào)用了第一個(gè)構(gòu)造函數(shù)贮泞,并創(chuàng)建一個(gè)ResponseDelivery楞慈。這個(gè)ResponseDelivery是回調(diào)的關(guān)鍵。我們看一下它的源碼啃擦。
ResponseDelivery
它是一個(gè)接口囊蓝,定義了三個(gè)方法:
public interface ResponseDelivery {
/** Parses a response from the network or cache and delivers it. */
void postResponse(Request<?> request, Response<?> response);
/**
* Parses a response from the network or cache and delivers it. The provided Runnable will be
* executed after delivery.
*/
void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/** Posts an error for the given request. */
void postError(Request<?> request, VolleyError error);
}
- void postResponse(Request<?> request, Response<?> response);
解析來(lái)自網(wǎng)絡(luò)或緩存的響應(yīng),并傳送到它 - void postResponse(Request<?> request, Response<?> response, Runnable runnable);
同上令蛉,不同的是聚霜,多一個(gè)參數(shù)Runnable,這個(gè)Runnable將會(huì)在傳送后被執(zhí)行 - void postError(Request<?> request, VolleyError error);
為指定的請(qǐng)求發(fā)送一個(gè)錯(cuò)誤
實(shí)際上珠叔,我們使用的ExecutorDelivery蝎宇,它是ResponseDelivery的實(shí)現(xiàn)類(lèi)。
import android.os.Handler;
import java.util.concurrent.Executor;
/** Delivers responses and errors. */
public class ExecutorDelivery implements ResponseDelivery {
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster;
/**
* Creates a new response delivery interface.
*
* @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(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));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
/** A Runnable used for delivering network responses to a listener on the main thread. */
@SuppressWarnings("rawtypes")
private static 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() {
// NOTE: If cancel() is called off the thread that we're currently running in (by
// default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
// won't be called, since it may be canceled after we check isCanceled() but before we
// deliver the response. Apps concerned about this guarantee must either call cancel()
// from the same thread or implement their own guarantee about not invoking their
// listener after cancel() has been called.
// 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()) {
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();
}
}
}
}
- 構(gòu)造函數(shù)
老規(guī)矩祷安,我們先看一看構(gòu)造函數(shù)姥芥。它有兩個(gè)構(gòu)造函數(shù),如下:
/**
* Creates a new response delivery interface.
*
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler)
/**
* Creates a new response delivery interface, mockable version for testing.
*
* @param executor For running delivery tasks
*/
public ExecutorDelivery(Executor executor)
區(qū)別是汇鞭,一個(gè)傳入了Handler凉唐,一個(gè)是傳入了Executor。目的是創(chuàng)建一個(gè)Executor霍骄,用于傳遞請(qǐng)求台囱,典型的作用是傳到主線(xiàn)程。Show you the code.
/**
* Creates a new response delivery interface.
*
* @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(command);
}
};
}
看到?jīng)]读整,我們?cè)趧?chuàng)建的Executor的execute方法中簿训,使用handler.post()方式,將runnable放到handler所在線(xiàn)程執(zhí)行绘沉。至于另外一個(gè)構(gòu)造函數(shù)煎楣,就一行代碼,將傳入的參數(shù)賦值給mResponsePoster车伞。
- ResponseDeliveryRunnable
一個(gè)用來(lái)將網(wǎng)絡(luò)請(qǐng)求的結(jié)果傳送到在主線(xiàn)程的回調(diào)監(jiān)聽(tīng)者中的Runnable择懂。功能簡(jiǎn)單但很重要,代碼不長(zhǎng)另玖,準(zhǔn)確地詮釋了簡(jiǎn)單卻不簡(jiǎn)單困曙。
/** A Runnable used for delivering network responses to a listener on the main thread. */
@SuppressWarnings("rawtypes")
private static 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() {
// NOTE: If cancel() is called off the thread that we're currently running in (by
// default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
// won't be called, since it may be canceled after we check isCanceled() but before we
// deliver the response. Apps concerned about this guarantee must either call cancel()
// from the same thread or implement their own guarantee about not invoking their
// listener after cancel() has been called.
// 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()) {
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();
}
}
}
從構(gòu)造方法說(shuō)開(kāi),它有三個(gè)參數(shù)谦去,分別是Request慷丽,Response,Runnable鳄哭。作用就是請(qǐng)求信息要糊、響應(yīng)信息和一個(gè)Runnable。這個(gè)方法比較簡(jiǎn)單妆丘,就是分別賦值給內(nèi)部變量锄俄。
然后局劲,最重要的是run()方法。在這個(gè)方法中奶赠,我們進(jìn)行了一些判斷鱼填。首先判斷請(qǐng)求是不是被取消了,如果被取消了毅戈,調(diào)用mRequest.finish("canceled-at-delivery")方法苹丸,結(jié)束請(qǐng)求,并不傳遞到主線(xiàn)程(不產(chǎn)生回調(diào))苇经。
接著判斷得到的響應(yīng)結(jié)果是不是成功的赘理,如果是,則調(diào)用mRequest.deliverResponse()方法扇单,將MResponse.result內(nèi)容傳遞到handler線(xiàn)程感憾。如果沒(méi)有成功,則調(diào)用mRequest.deliverError()將錯(cuò)誤內(nèi)容mResponse.error傳遞到handler線(xiàn)程令花。如果發(fā)現(xiàn)設(shè)置了一個(gè)Runnable阻桅,就調(diào)用它的run()方法。
- 實(shí)現(xiàn)ResponseDelivery的三個(gè)方法
@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));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
比較簡(jiǎn)單兼都,就不詳細(xì)講解了嫂沉。
NetworkDispatcher
回到RequestQueue的構(gòu)造方法,我們知道扮碧,它還創(chuàng)建了一個(gè)工作線(xiàn)程池趟章。這就需要分析下NetworkDispatcher類(lèi)。先看一下類(lèi)描述慎王。
Provides a thread for performing network dispatch from a queue of requests.
<p>Requests added to the specified queue are processed from the network via a specified {@link
Network} interface. Responses are committed to cache, if eligible, using a specified {@link
Cache} interface. Valid responses and errors are posted back to the caller via a {@link
ResponseDelivery}.
意思就是提供用于從請(qǐng)求隊(duì)列執(zhí)行網(wǎng)絡(luò)分派的線(xiàn)程蚓土。所以,這個(gè)類(lèi)是繼承于線(xiàn)程的赖淤。
這個(gè)類(lèi)有一個(gè)構(gòu)造函數(shù)
/**
* 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;
}
從注釋中蜀漆,我們看到,它用來(lái)創(chuàng)建一個(gè)新的網(wǎng)絡(luò)派發(fā)線(xiàn)程咱旱,你需要調(diào)用start()方法來(lái)讓它運(yùn)行确丢。有四個(gè)參數(shù),分別是:queue吐限,network鲜侥,cache和delivery。queue是傳入的分類(lèi)請(qǐng)求的隊(duì)列诸典;network是用于實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求描函;cache是用于將網(wǎng)絡(luò)響應(yīng)寫(xiě)入緩存;delivery用于遞送響應(yīng)結(jié)果。
再看看線(xiàn)程必須要重寫(xiě)的方法:run()舀寓。
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
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 NetworkDispatcher thread; "
+ "use quit() to terminate it");
}
}
}
首先調(diào)用Process.setThreadPriority將線(xiàn)程設(shè)置為Process.THREAD_PRIORITY_BACKGROUND益缠。在這里列一下Android的線(xiàn)程優(yōu)先級(jí)級(jí)別:
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //設(shè)置線(xiàn)程優(yōu)先級(jí)為后臺(tái),這樣當(dāng)多個(gè)線(xiàn)程并發(fā)后很多無(wú)關(guān)緊要的線(xiàn)程分配的CPU時(shí)間將會(huì)減少基公,有利于主線(xiàn)程的處理,有以下幾種:
int THREAD_PRIORITY_AUDIO //標(biāo)準(zhǔn)音樂(lè)播放使用的線(xiàn)程優(yōu)先級(jí)
int THREAD_PRIORITY_BACKGROUND //標(biāo)準(zhǔn)后臺(tái)程序
int THREAD_PRIORITY_DEFAULT // 默認(rèn)應(yīng)用的優(yōu)先級(jí)
int THREAD_PRIORITY_DISPLAY //標(biāo)準(zhǔn)顯示系統(tǒng)優(yōu)先級(jí)宋欺,主要是改善UI的刷新
int THREAD_PRIORITY_FOREGROUND //標(biāo)準(zhǔn)前臺(tái)線(xiàn)程優(yōu)先級(jí)
int THREAD_PRIORITY_LESS_FAVORABLE //低于favorable
int THREAD_PRIORITY_LOWEST //有效的線(xiàn)程最低的優(yōu)先級(jí)
int THREAD_PRIORITY_MORE_FAVORABLE //高于favorable
int THREAD_PRIORITY_URGENT_AUDIO //標(biāo)準(zhǔn)較重要音頻播放優(yōu)先級(jí)
int THREAD_PRIORITY_URGENT_DISPLAY //標(biāo)準(zhǔn)較重要顯示優(yōu)先級(jí)轰豆,對(duì)于輸入事件同樣適用。
接下來(lái)齿诞,調(diào)用了內(nèi)部方法processRequest()酸休。說(shuō)明:提取到自己的方法,以確保本地有一個(gè)受GC限制的活躍范圍祷杈。這是為了避免在不確定的時(shí)間內(nèi)保持先前的請(qǐng)求引用存活斑司。
@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());
}
}
// Extracted to its own method to ensure locals have a constrained liveness scope by the GC.
// This is needed to avoid keeping previous request references alive for an indeterminate amount
// of time. Update consumer-proguard-rules.pro when modifying this. See also
// https://github.com/google/volley/issues/114
private void processRequest() throws InterruptedException {
// Take a request from the queue.
Request<?> request = mQueue.take();
processRequest(request);
}
@VisibleForTesting
void processRequest(Request<?> request) {
// 獲取從設(shè)備boot后經(jīng)歷的時(shí)間值
long startTimeMs = SystemClock.elapsedRealtime();
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.
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.
request.markDelivered();
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
request.notifyListenerResponseNotUsable();
} 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);
request.notifyListenerResponseNotUsable();
}
}
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
可以看出來(lái),所有的處理都是在這個(gè)方法中進(jìn)行的:void processRequest(Request<?> request)但汞;
我們一行一行的來(lái)分析宿刮。
第一行割按,取出獲取從設(shè)備boot后經(jīng)歷的時(shí)間值;
long startTimeMs = SystemClock.elapsedRealtime();
第二行貌夕,給request添加一個(gè)Marker。作用是: Adds an event to this request's event log; for debugging. 不重要食绿,所以踩叭,我們不管它磕潮。
request.addMarker("network-queue-take");
然后接下來(lái)的幾行的作用是,判斷這個(gè)請(qǐng)求是不是被cancel了容贝。如果是自脯,則不進(jìn)行網(wǎng)絡(luò)請(qǐng)求,同時(shí)發(fā)送本請(qǐng)求不可用通知斤富。
// If the request was cancelled already, do not perform the
// network request.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}
接下來(lái)是為這筆請(qǐng)求設(shè)置流量統(tǒng)計(jì)Tag膏潮。關(guān)于TrafficStats類(lèi)的用法,可以參考這篇文章满力,就不當(dāng)搬運(yùn)工了戏罢。我是任意門(mén)
addTrafficStatsTag(request);
再下來(lái)就是重點(diǎn): 網(wǎng)絡(luò)請(qǐng)求。調(diào)用Network.performRequest()方法完成網(wǎng)絡(luò)請(qǐng)求脚囊,注意這個(gè)是阻塞的龟糕,所以,執(zhí)行完畢之后悔耘,我們就給這個(gè)request設(shè)置了一個(gè)標(biāo)志:network-http-complete讲岁。
// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
然后再判斷是不是上次請(qǐng)求的內(nèi)容沒(méi)有發(fā)生變化(返回代碼304),并且響應(yīng)已經(jīng)被傳遞了,就以“not-modified”結(jié)束缓艳,同時(shí)發(fā)送本請(qǐng)求不可用通知校摩。
// 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");
request.notifyListenerResponseNotUsable();
return;
}
接下來(lái)是最精彩的地方,我們的網(wǎng)絡(luò)請(qǐng)求結(jié)果就是這樣轉(zhuǎn)換成我們想要的對(duì)象的阶淘。如何實(shí)現(xiàn)的呢衙吩?通過(guò)調(diào)用reques.parseNetworkResponse()。
// Parse the response here on the worker thread.
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
然后是緩存溪窒。
// Write to cache if applicable.如果適用坤塞,寫(xiě)入緩存。
// 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");
}
最最最關(guān)鍵的部分來(lái)了~~~ 澈蚌,緩存之后摹芙,我們需要將解析之后的結(jié)果傳送到主線(xiàn)程。首先宛瞄,設(shè)置標(biāo)志浮禾,然后post出去,最后發(fā)送響應(yīng)收到通知份汗。
// Post the response back.
request.markDelivered();
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
--M: 好盈电,打完收工。
--Y: 咦杯活,不對(duì)挣轨,怎么能沒(méi)有錯(cuò)誤處理?
--M: 對(duì)轩猩,你很機(jī)智卷扮。
我們?cè)倏纯串惓!均践?偣瞔atch了兩個(gè)異常晤锹。先看一個(gè)異常處理方法。在這個(gè)方法中彤委,request調(diào)用parseNetworkError解析了網(wǎng)絡(luò)異常鞭铆。然后,調(diào)用Delivery的postError方法焦影,將錯(cuò)誤傳遞到主線(xiàn)程车遂。
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
- VolleyError
設(shè)置網(wǎng)絡(luò)請(qǐng)求的總時(shí)間,然后翻譯錯(cuò)誤原因并傳遞到主線(xiàn)程斯辰,最后發(fā)送一個(gè)通知舶担。volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); request.notifyListenerResponseNotUsable();
- Exception
代碼比較簡(jiǎn)單,根據(jù)Exception創(chuàng)建一個(gè)VolleyError彬呻,然后設(shè)置網(wǎng)絡(luò)請(qǐng)求的總時(shí)間衣陶。然后傳遞到主線(xiàn)程柄瑰。最后發(fā)送一個(gè)通知。VolleyLog.e(e, "Unhandled exception %s", e.toString()); VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); request.notifyListenerResponseNotUsable();
好了剪况,Network類(lèi)解析完成教沾,一個(gè)完整的網(wǎng)絡(luò)請(qǐng)求過(guò)程也捋清楚了。
我們?cè)賮?lái)看一張圖译断,捋一捋Volley的工作流授翻。