Volley是Google I/O 2013上發(fā)布的一個網(wǎng)絡(luò)通信庫,本文將基于Android N Frameworks層中的Volley源碼進(jìn)行分析。
Volley框架介紹:
在計(jì)算機(jī)網(wǎng)絡(luò)發(fā)展中,誕生了兩種經(jīng)典的計(jì)算機(jī)網(wǎng)絡(luò)參考模型:OSI參考模型與TCP/IP參考模型,其中的分層如下所示:
| OSI七層模型 | TCP/IP四層模型 | 傳輸?shù)臄?shù)據(jù) |
|---|---|---|
| 應(yīng)用層 | 應(yīng)用層 | 數(shù)據(jù) |
| 表示層 | 應(yīng)用層 | 數(shù)據(jù) |
| 會話層 | 應(yīng)用層 | 數(shù)據(jù) |
| 傳輸層 | 傳輸層 | 段 |
| 網(wǎng)絡(luò)層 | 網(wǎng)際互聯(lián)層 | 包 |
| 數(shù)據(jù)鏈路層 | 網(wǎng)絡(luò)接入層 | 幀 |
| 物理層 | 網(wǎng)絡(luò)接入層 | 比特流 |
而目前Android開發(fā)中常提及的各種網(wǎng)絡(luò)框架面向的網(wǎng)絡(luò)層也是不同的,針對TCP/IP四層模型中,現(xiàn)在使用較多的OKHttp,HttpClient, URLConnection都是面向傳輸層的網(wǎng)絡(luò)框架,而Retrofit, AsyncHttp, Volley則是依托于這些面向傳輸層的網(wǎng)絡(luò)框架。其中Retrofit中默認(rèn)采用OKHttp作為其網(wǎng)絡(luò)傳輸層,AsyncHttp則默認(rèn)采用HttpClient,而Volley中,對于API Level大于8的采用URLConnection作為網(wǎng)絡(luò)傳輸層,而小于等于8的采用HttpClient作為網(wǎng)絡(luò)傳輸層,同時它支持定義OKHttp作為其網(wǎng)絡(luò)傳輸層。對于一些說Volley是基于OKHttp進(jìn)行封裝的網(wǎng)絡(luò)庫,這種說法其實(shí)并不準(zhǔn)確。
Volley簡單使用
Volley的使用大致分為三步:請求初始化、構(gòu)建請求參數(shù)、設(shè)置請求回調(diào)、執(zhí)行請求。如下所示是簡單的Volley請求寫法。
RequestQueue mQueue = Volley.newRequestQueue(context);
StringRequest stringRequest = new StringRequest(url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
mQueue.add(stringRequest);
Volley源碼分析
Android 7.0 源碼中關(guān)于Volley的庫位于frameworks/volley包下。現(xiàn)我們將按Volley的使用步驟進(jìn)行源碼的分析:
Volly RequestQueue的初始化
Volley調(diào)用的newRequestQueue有兩類構(gòu)造方法:newRequestQueue(Context context)與newRequestQueue(Context context, HttpStack stack),如果不選擇傳入HttpStack,則Volley將以傳入null的形式調(diào)用后一方法。其具體實(shí)現(xiàn)如下所示:
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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
我們可以看到,對于stack的處理:如果默認(rèn)傳入HttpStack的實(shí)例stack為空,則會根據(jù)SDK API版本選擇不同的實(shí)現(xiàn)類.其中HurlStack與HttpClientStack都是HttpStack的實(shí)現(xiàn)類,從名稱上以可以看的出來,HurlStack是封裝的HttpURLConnection,而HttpClientStack是封裝的HttpClient。其具體的封裝與請求使用我會在其他文章中進(jìn)行詳細(xì)分析。此處不再詳述。
BasicNetWork是Network接口的實(shí)現(xiàn)類,其中接口的實(shí)現(xiàn)方法performRequest(Request<?> request)中會調(diào)用請求Request去真正執(zhí)行網(wǎng)絡(luò)請求。此處的start()方法只是調(diào)用RequestQueue的start()方法啟動CacheDispatcher與NetRequestDispatcher兩個請求轉(zhuǎn)發(fā)線程,具體請參考后文的RequestQueue的start方法分析。
Volley RequestQueue的構(gòu)造函數(shù)
在RequestQueue中start()方法啟動前,會調(diào)用RequestQueue構(gòu)造方法進(jìn)行初始化,將NetWork實(shí)現(xiàn)類和默認(rèn)的緩存?zhèn)魅隦equestQueue中,完成RequestQueue的初始化。其中最終調(diào)用的初始化方法如下所示:
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
在構(gòu)造方法中,RequestQueue中對傳入各部分的參數(shù)注釋如下:
- cache A Cache to use for persisting responses to disk (Cache 是一個接口定義,默認(rèn)使用DiskCache來持久化響應(yīng)結(jié)果)
- network A Network interface for performing HTTP requests (Network 是一個接口定義,默認(rèn)使用BasicNetwork來執(zhí)行HTTP請求)
- threadPoolSize Number of network dispatcher threads to create (使用NetworkDispatcher數(shù)組定義執(zhí)行器容器,默認(rèn)容器最大容量為4)
- delivery A ResponseDelivery interface for posting responses and errors (請求結(jié)果轉(zhuǎn)發(fā)接口,默認(rèn)使用ExecutorDelivery轉(zhuǎn)發(fā)結(jié)果)
在構(gòu)造函數(shù)之前的注釋說明的更清楚一點(diǎn):Creates the worker pool. Processing will not begin until {@link #start()} is called.,大意為創(chuàng)建工作池,但是在調(diào)用start方法之前,整個請求并不會真正開始工作。那么接下來我們便來分析RequestQueue的啟動方法start()。
Volley RequestQueue的啟動
RequestQueue中的start()方法中,會真正的開始執(zhí)行一個請求隊(duì)列,而在這之中,RequestQueue又會引入二個新的類型,分別為:CacheDispatcher與NetworkDispatcher,具體代碼如下所示:
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();
}
}
在啟動任務(wù)之前會調(diào)用stop()方法確保當(dāng)前沒有正在運(yùn)行任務(wù)這個比較好理解,而接下來創(chuàng)建的CacheDispatcher從構(gòu)造方法傳參看,其中又多了一個未曾在前面出現(xiàn)過的mCacheQueue,NetWorkDispatcher也多了一個未曾在前面出現(xiàn)過的mNetworkQueue。這兩個集合類是在類變量聲明時便進(jìn)行初始化了:
private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();
private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();
CacheDispatcher繼承自Thread,從名稱就可以看出,主要作用是管理緩存派發(fā)請求的。而NetworkDispatcher同樣也繼承自Thread,是處理緩存未派發(fā)的請求的。當(dāng)調(diào)用其start()方法后,線程便會啟動。我會在其他文章中對它的緩存派發(fā)和請求管理進(jìn)行詳細(xì)分析。此處不再詳述
Volley 構(gòu)建Request請求與注冊監(jiān)聽
在Volley對RequestQueue初始化完畢后,我們需要構(gòu)建請求,來完成我們的網(wǎng)絡(luò)請求.在簡單使用的代碼中,構(gòu)建請求使用的是StringRequest,它是抽象類Request的子類。Request的子類請求還有ImageRequest、JsonRequest。而JsonRequest是JsonArrayRequest與JsonObjectRequest的父類。對于Request類中,我們注意到,對于Request這個頂級父類,其監(jiān)聽只注冊了一個ErrorListener,而結(jié)果監(jiān)聽則交給其實(shí)現(xiàn)類去注冊,如StringRequest的結(jié)果監(jiān)聽為Listener<String>、ImageRequest的結(jié)果監(jiān)聽為Listener<Bitmap>、JsonArrayRequest注冊的結(jié)果監(jiān)聽為Listener<JSONArray>、JsonObjectRequest注冊的結(jié)果監(jiān)聽為Listener<JSONObject>。而JsonRequest的結(jié)果監(jiān)聽卻支持范型傳入Listener<T>,從這點(diǎn)來說,筆者有些奇怪,既然支持范型定義,為何不直接在頂級父類定義封裝,然后在子類中傳入?yún)?shù)進(jìn)行初始化使用呢? 從軟件分層的角度來看,好像這樣也更合理。希望對這個問題有想法的同學(xué)能提供些思路給我。
而對于不同的Request子類實(shí)現(xiàn)類,筆者會在以后對幾種實(shí)現(xiàn)類進(jìn)行詳細(xì)分析。大致概括說來,Volley會對請求結(jié)果進(jìn)行解析封裝,以對應(yīng)的類型包裝回調(diào)回來,其是由抽象類中的deliverResponse()返回,其代碼如下所示:
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
而在 RequestQueue的初始化 中我們說過,BasicNetWork中的performRequest()方法是會去執(zhí)行Request的網(wǎng)絡(luò)請求,那它是要如何請求呢,這就需要我們將構(gòu)建的請求與ReqeustQueue及其相關(guān)組件關(guān)聯(lián)起來了,這就是我們下一步要介紹的RequestQueue的add()方法。
RequestQueue添加Request請求
在使用對應(yīng)的Request構(gòu)建好網(wǎng)絡(luò)請求后,RequestQueue會調(diào)用其add()方法將構(gòu)建的請求加入到請求隊(duì)列中,其具體執(zhí)行方法代碼如下所示:
// 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<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
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 {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
從代碼中可以看到,如果Request不允許緩存的話,Request直接加入到NetWorkQueue中就直接return了,而默認(rèn)request是允許使用緩存的,當(dāng)允許使用緩存時,如果WaitingRequestsQueue中沒有相同的請求任務(wù),則會將任務(wù)key加入到waitingRquests中,但是傳入隊(duì)列為空,然后將任務(wù)加入CacheQueue中。如果已經(jīng)有同樣的請求在運(yùn)行,則會將新任務(wù)構(gòu)建隊(duì)列,加入到WaitingRequest中,但并不不執(zhí)行。
將Request加入到CacheQueue中后,我們留意到CacheQueue后續(xù)沒有操作了,這個原因在于,CacheQueue與前文中描述的CacheDispatcher耦合,CacheDispatcher作為一個線程死循環(huán),在RequestQueue中已經(jīng)啟動,它會不停的遍歷CacheQueue中的Request請求。其部分主要代碼如下所示:
// 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);
}
如果緩存未命中或者緩存過期,則會調(diào)用NetworkQueue進(jìn)行請求。如果緩存命中,則會直接調(diào)用ResponseDelivery(默認(rèn)為其實(shí)現(xiàn)類ExecutorDelivery)進(jìn)行結(jié)果分發(fā)。對于NetworkQueue.post(Request)的執(zhí)行結(jié)果,最后會由NetworkDispatcher去執(zhí)行,最后也會將執(zhí)行結(jié)果通過ResponseDelivery調(diào)用postResponse(reqeust, response)將結(jié)果返回(特別的對于reponse的結(jié)果CacheDispatcher和NetworkDispathcer都會調(diào)用Request.ParseNetworkResponse()方法來解析Response結(jié)果。這樣處理的結(jié)果就是,在回調(diào)listenter中,Response回調(diào)的就是已經(jīng)處理過的類型,而不用再去做一次數(shù)據(jù)解析,相關(guān)示例會在對于圖片請求源碼分析中進(jìn)行說明)。
Volley Request的請求回調(diào)
在ExecutorDelivery調(diào)用postResponse方法后,ExecutorDelivery將以handler.post(runnable)的形式來執(zhí)行調(diào)用的request與response,其關(guān)鍵構(gòu)建方法如下:
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);
}
};
}
其中,ExecutorDelivery調(diào)用的postResponse方法中所執(zhí)行的Runnable對象為內(nèi)部類ResponseDeliveryRunnable,其執(zhí)行工作主要是將結(jié)果回調(diào)到Request中,其關(guān)鍵代碼如下所示:
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
在回調(diào)到Request中后,最后會通過祖冊的listener將result回調(diào)回來,以StringRequest如下所示:
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
這樣一個完整的Volley 網(wǎng)絡(luò)請求也就完成了。最后附上一張簡單的流程圖,隨著分析完善,圖中缺失的部分也會慢慢補(bǔ)齊

對于本文中的描述有什么疑問或者建議都可以留言提出,歡迎大家批評指導(dǎo)。O(∩_∩)O哈哈哈~
對于Volley的初步源碼分析我們也暫告一段落,在這篇文章中所遺留的Volley的異步圖片加載支持、緩存策略、Request子類分類請求區(qū)別以及Volley采用OkHttp作為網(wǎng)絡(luò)傳輸層等都會在后面文章繼續(xù)分析。
對于圖片加載的源碼分析已經(jīng)更新,詳情點(diǎn)擊:
對于網(wǎng)絡(luò)請求緩存的源碼分析已經(jīng)更新,詳情點(diǎn)擊: