從源碼分析Android的Volley庫的工作流程
Volley現(xiàn)在已經(jīng)被官方放到AOSP里面,已經(jīng)逐步成為Android官方推薦的網(wǎng)絡(luò)框架。
類抽象
對Http協(xié)議的抽象
Requeset
顧名思義,對請求的封裝,實現(xiàn)了Comparable接口,因為在Volley中是可以指定請求的優(yōu)先級的,實現(xiàn)Comparable是為了在Request任務(wù)隊列中進(jìn)行排序,優(yōu)先級高的Request會被優(yōu)先調(diào)度執(zhí)行。
NetworkResponse
Http響應(yīng)的封裝,其中包括返回的狀態(tài)碼 頭部 數(shù)據(jù)等。
Response
給調(diào)用者返回的結(jié)果封裝,它比NetworkResponse更加簡單,只包含三個東西:數(shù)據(jù) 異常 和 Cache數(shù)據(jù)。
Network
對HttpClient的抽象,接受一個Request,返回一個NetworkResponse
反序列化抽象
所謂反序列化,就是將網(wǎng)絡(luò)中傳輸?shù)膶ο笞兂梢粋€Java對象,Volley中是通過擴(kuò)展Request類來實現(xiàn)不同的反序列化功能,如JsonRequest StringRequest,我們也可以通過自己擴(kuò)展一些Request子類,來實現(xiàn)對請求流的各種定制。
請求工作流抽象
RequestQueue
用來管理各種請求隊列,其中包含有4個隊列
a) 所有請求集合,通過RequestQueue.add()添加的Request都會被添加進(jìn)來,當(dāng)請求結(jié)束之后刪除。
b) 所有等待Request,這是Volley做的一點(diǎn)優(yōu)化,想象一下,我們同時發(fā)出了三個一模一樣的Request,此時底層其實不必真正走三個網(wǎng)絡(luò)請求,而只需要走一個請求即可。所以Request1被add之后會被調(diào)度執(zhí)行,而Request2 和Request3被加進(jìn)來時,如果Request1還未執(zhí)行完畢,那么Request2和 Request3只需要等著Request1的結(jié)果即可。
c) 緩存隊列,其中的Request需要執(zhí)行查找緩存的工作
d) 網(wǎng)絡(luò)工作隊列 其中的Request需要被執(zhí)行網(wǎng)絡(luò)請求的工作
NetworkDispatcher
執(zhí)行網(wǎng)絡(luò)Request的線程,它會從網(wǎng)絡(luò)工作隊列中取出一個請求,并執(zhí)行。Volley默認(rèn)有四個線程作為執(zhí)行網(wǎng)絡(luò)請求的線程。
CacheDispatcher
執(zhí)行Cache查找的線程,它會從緩存隊列中取出一個請求,然后查找該請求的本地緩存。Volley只有一個線程執(zhí)行Cache任務(wù)。
ResponseDelivery
請求數(shù)據(jù)分發(fā)器,可以發(fā)布Request執(zhí)行的結(jié)果。
Cache
對Cache的封裝,主要定義了如何存儲,獲取緩存,存取依據(jù)Request中的getCacheKey()來描述。
提交請求
Volley通過RequestQueue.add(Request)來往任務(wù)隊列中增加請求:
一個Request被提交之后有幾個去處:
1.Set<Request<?>> mCurrentRequests對應(yīng)所有請求隊列。所有調(diào)用add的Request必然都會添加到這里面來。
2.PriorityBlockingQueue<Request<?>> mNetworkQueue 對應(yīng)網(wǎng)絡(luò)隊列。如果一個Request不需要緩存,那么add之后會被直接添加到網(wǎng)絡(luò)隊列中。
3.PriorityBlockingQueue<Request<?>> mCacheQueue對應(yīng)緩存請求。如果一個Request需要緩存,并且當(dāng)前的RequestQueue中并沒有一個Request的getCacheKey和當(dāng)前Request相同(可以認(rèn)為一個請求),那么加入緩存隊列,讓緩存工作線程來處理。
4.Map<String, Queue<Request<?>>> mWaitingRequests對應(yīng)等待隊列。如果RequestQueue中已經(jīng)有一個相同請求在處理,這里只需要將這個Request放到等待隊列中,等之前的Request結(jié)果回來之后,進(jìn)行處理即可。
Volley提交任務(wù)到隊列中是不是很簡單?下面來說說優(yōu)先級請求的事情吧,你可能已經(jīng)注意到了,上面兩個存放需要執(zhí)行任務(wù)的隊列都是PriorityBlockingQueue,前面說了Request現(xiàn)實了Comparable,看看這個方法:
@Override public int compareTo(Request<T> other) { Priority left = this.getPriority(); Priority right = other.getPriority(); //mSequence表示請求序列號,add時,會通過一個計數(shù)器來指定 return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal(); }
所以,如果我們的工作線程(NetworkDispatcher,CacheDispatcher)取任務(wù)時,自然會從頭部開始取。
這里的優(yōu)先級,僅僅是保證一個請求比另外一個請求先處理,而并不能保證一個高優(yōu)先級請求一定會比低優(yōu)先級的請求先回來
緩存工作線程處理
@Override public void run() { //初始化Cache mCache.initialize(); Request<?> request; while (true) { //阻塞 獲取一個Cache任務(wù) request = mCacheQueue.take(); try { //已經(jīng)被取消 if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } //如果拿cache未果,放入網(wǎng)絡(luò)請求隊列 Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { request.addMarker("cache-miss"); mNetworkQueue.put(request); continue; } //緩存超時,放入網(wǎng)絡(luò)請求隊列 if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } //根據(jù)Cache構(gòu)造Response Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); //是否超過軟過期 if (!entry.refreshNeeded()) { // 直接返回Cache mDelivery.postResponse(request, response); } else { request.setCacheEntry(entry); //設(shè)置中間結(jié)果 response.intermediate = true; //發(fā)送中間結(jié)果 final Request<?> finalRequest = request; mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { //中間結(jié)果完事之后,講請求放入網(wǎng)絡(luò)隊列 mNetworkQueue.put(finalRequest); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (Exception e) { } } }
這里可以看到Volley確實對緩存封裝很到位,各種情況都考慮到了,其中比較重要的兩點(diǎn):
取出來的Cache并不僅僅是數(shù)據(jù),同時還包括這次請求的一些Header
硬過期 軟過期
我們可以看到Cache中有兩個字段來描述緩存過期: Cache.ttl vs Cache.softTtl。什么區(qū)別呢?如果ttl過期,那么這個緩存永遠(yuǎn)不會被使用了;如果softTtl沒有過期,這個數(shù)據(jù)直接返回;如果softTtl過期,那么這次請求將有兩次返回,第一次返回這個Cahce,第二次返回網(wǎng)絡(luò)請求的結(jié)果。想想,這個是不是滿足我們很多場景呢?先進(jìn)入頁面展示緩存,然后再刷新頁面;如果這個緩存太久了,可以等待網(wǎng)絡(luò)數(shù)據(jù)回來之后再展示數(shù)據(jù),是不是很贊?
NetworkDispatcher
執(zhí)行網(wǎng)絡(luò)請求的工作線程,默認(rèn)有4個線程,它不停地從網(wǎng)絡(luò)隊列中取任務(wù)執(zhí)行。
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 { 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; } //通過Http棧實現(xiàn)客戶端發(fā)送網(wǎng)絡(luò)請求 NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // 如果緩存軟過期,那么會重新走網(wǎng)絡(luò);如果server返回304,表示上次之后請求結(jié)果數(shù)據(jù)本地并沒有過期,所以可以直接用本地的,因為之前Volley已經(jīng)發(fā)過一次Response了,所以這里就不需要再發(fā)送Response結(jié)果了。 if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); //更新緩存 if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } //發(fā)送結(jié)果 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); } } }
Request
Request中主要封裝了一個請求的各類Http協(xié)議信息,比如 URL,請求方法,請求的優(yōu)先級,請求重試的策略,緩存策略等。
這里說一下其中比較有意思的重發(fā)策略,如果一次請求發(fā)生超時異常,比如SocketTimeoutException ConnectTimeoutException ,我們可以為Request配置一個RetryPolicy,你可以指定重發(fā)這個Request的次數(shù),以及每次失敗之后重新設(shè)置這個請求的超時時間(第一次失敗之后,你可以調(diào)整第二次請求的超時時間增加,以減少失敗的可能性)。
反序列化
Request最重要的功能就是提供了內(nèi)容的反序列化,通過不同的子類來實現(xiàn)不同的序列化功能。比如,如果請求結(jié)果是一個Json的對象,我們可以使用JsonObjectRequest,如果是一個普通字符,使用StringRequest,同時,我們也可以很方便的定制自己的Request,通過復(fù)寫Response<T> parseNetworkResponse(NetworkResponse response);方法即可。
默認(rèn)的JsonRequest使用org.json中的Json解析,我們使用Gson來進(jìn)行解析能夠構(gòu)造一個更加通用的處理json返回的Request:
public class JsonGRequest<T> extends Request<T> { private static Gson gson = new Gson(); private Response.Listener<T> mListener; public JsonGRequest(String url, Response.ErrorListener listener,Response.Listener responseListener) { super(url, listener); this.mListener = mListener; } public JsonGRequest(int method, String url, Response.ErrorListener listener) { super(method, url, listener); } @Override protected Response<T> parseNetworkResponse(NetworkResponse response) { return Response.success(gson.fromJson(new InputStreamReader(new ByteArrayInputStream(response.data)),getType()), HttpHeaderParser.parseCacheHeaders(response)) } @Override protected void deliverResponse(T response) { if(mListener != null) { mListener.onResponse(response); } } //獲取指定的泛型類型 protected Type getType() { Type superclass; for(superclass = this.getClass().getGenericSuperclass(); superclass instanceof Class && !superclass.equals(JsonGRequest.class); superclass = ((Class)superclass).getGenericSuperclass()) { ; } if(superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } else { ParameterizedType parameterized = (ParameterizedType)superclass; return parameterized.getActualTypeArguments()[0]; } } }
ImageRequest
Volley專門為圖片請求提供了ImageRequest,主要是反序列化了一下數(shù)據(jù)流到BitMap,還可以制定圖片的大小,質(zhì)量等參數(shù)。
ImageLoader是Volley提供的一個用來加載圖片的工具,它的內(nèi)部還是使用ImageRequest來實現(xiàn)的,主要新加的功能是增加了內(nèi)存緩存,你可以通過配置ImageCache來設(shè)置內(nèi)存緩存。
相關(guān)文章
android開發(fā)通過Scroller實現(xiàn)過渡滑動效果操作示例
這篇文章主要介紹了android開發(fā)通過Scroller實現(xiàn)過渡滑動效果,結(jié)合實例形式分析了Android Scroller類實現(xiàn)過渡滑動效果的基本原理與實現(xiàn)技巧,需要的朋友可以參考下2020-01-01Android定時器實現(xiàn)定時執(zhí)行、重復(fù)執(zhí)行、定時重復(fù)執(zhí)行、定次數(shù)執(zhí)行的多種方式
今天小編就為大家分享一篇關(guān)于Android定時器實現(xiàn)定時執(zhí)行、重復(fù)執(zhí)行、定時重復(fù)執(zhí)行、定次數(shù)執(zhí)行的多種方式,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12解決android報錯:Intel HAXM is required to run this AVD
這篇文章主要介紹了解決android報錯:Intel HAXM is required to run this AVD,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11DCloud的native.js調(diào)用系統(tǒng)分享實例Android版代碼
本文為大家分享了DCloud的native.js如何調(diào)用系統(tǒng)分享功能Android版的實例代碼,直接拿來就用2018-09-09Android 動畫之TranslateAnimation應(yīng)用詳解
本節(jié)講解TranslateAnimation動畫,TranslateAnimation比較常用,比如QQ,網(wǎng)易新聞菜單條的動畫,就可以用TranslateAnimation實現(xiàn),本文將詳細(xì)介紹通過TranslateAnimation 來定義動畫,需要的朋友可以參考下2012-12-12Kotlin基礎(chǔ)學(xué)習(xí)之lambda中return語句詳解
這篇文章主要給大家介紹了關(guān)于Kotlin基礎(chǔ)學(xué)習(xí)之lambda中return語句的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或使用Kotlin具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07