摘要:在大行其道的今天,已經(jīng)略顯過時(shí)。我們按照上面的思路,從和作為出發(fā)點(diǎn),由淺入深來閱讀源碼。在最后看類源碼時(shí)我們可以看到這個(gè)過程。負(fù)責(zé)分發(fā),對(duì)來說,就是通過將發(fā)送到了主線程在中調(diào)用了。的個(gè)數(shù),決定了網(wǎng)絡(luò)請(qǐng)求的最大并發(fā)數(shù)。
Volley
在 retrofit+okhttp 大行其道的今天,volley 已經(jīng)略顯過時(shí)。使用 volley,我們無(wú)法避免寫一些樣板代碼,但在它剛出現(xiàn)時(shí),曾經(jīng)很大程度降低了 android 開發(fā)中網(wǎng)絡(luò)請(qǐng)求這部分的難度,簡(jiǎn)潔、輕量、容易定制擴(kuò)展,我們依然能從它的源碼中感受到這些。
看一個(gè)使用 volley 的小 demo
StringRequest request = new StringRequest("http://example.com", new Response .Listener() { @Override public void onResponse(String s) { } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { } }); RequestQueue requestQueue = Volley.newRequestQueue(context); requestQueue.add(request);
三步走
創(chuàng)建一個(gè) Request
創(chuàng)建一個(gè) RequestQueue
聯(lián)結(jié)兩者
這就是 volley 作為一個(gè)網(wǎng)絡(luò)請(qǐng)求庫(kù)提供給我們的用戶界面,超級(jí)簡(jiǎn)潔有沒有。Request 是一個(gè)數(shù)據(jù),是一個(gè)相對(duì)靜態(tài)的概念,我們使用它來說我們的網(wǎng)絡(luò)請(qǐng)求要發(fā)送到哪里、帶有什么樣的參數(shù)、返回的數(shù)據(jù)是什么對(duì)象、請(qǐng)求成功了怎樣處理、請(qǐng)求失敗了又要怎么辦,等等等等。RequestQueue 呢,是我們的執(zhí)行引擎,吃掉一個(gè)個(gè) Request,消費(fèi) Request 的屬性,同時(shí)返回 Response。
我們按照上面的思路,從 Request 和 RequestQueue 作為出發(fā)點(diǎn),由淺入深來閱讀 volley 源碼。
Request前面說到,Request 在 volley 中是相對(duì)靜態(tài)的概念,靜態(tài)是好的,我們喜歡靜態(tài)的東西,所以我們從 Request 開始???Request 的定義
public abstract class Requestimplements Comparable > { }
我們看到,Request 是個(gè)抽象類,有泛型,實(shí)現(xiàn)了 Comparable 接口??吹竭@里你也許可以大膽推測(cè),這個(gè) Request 是可以大大擴(kuò)展的,大多數(shù)網(wǎng)絡(luò)請(qǐng)求以表單的方式作為參數(shù),十分多變,那么這個(gè)泛型來指名返回結(jié)果的類型應(yīng)該是十分合理的。沒錯(cuò),就是這樣。實(shí)現(xiàn) Comparable 呢,Request 的比較有什么含義?想到上面有 Queue 的概念,這個(gè) Comaparable 也許和請(qǐng)求的優(yōu)先級(jí)有關(guān),對(duì)的,事實(shí)上 RequestQueue 中的確使用了 PriorityBlockingQueue 來達(dá)到管理 Request 優(yōu)先級(jí)的效果,后文再細(xì)說。
上面簡(jiǎn)單的一行代碼已經(jīng)向我們透漏了大量信息,讓我們更近一步??纯?Request 有哪些屬性
public abstract class Requestimplements Comparable > { /** * Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS, * TRACE, and PATCH. */ private final int mMethod; /** URL of this request. */ private final String mUrl; /** Default tag for {@link TrafficStats}. */ private final int mDefaultTrafficStatsTag; /** Listener interface for errors. */ private final Response.ErrorListener mErrorListener; /** Sequence number of this request, used to enforce FIFO ordering. */ private Integer mSequence; /** The request queue this request is associated with. */ private RequestQueue mRequestQueue; /** Whether or not responses to this request should be cached. */ private boolean mShouldCache = true; /** Whether or not this request has been canceled. */ private boolean mCanceled = false; /** Whether or not a response has been delivered for this request yet. */ private boolean mResponseDelivered = false; /** Whether the request should be retried in the event of an HTTP 5xx (server) error. */ private boolean mShouldRetryServerErrors = false; /** The retry policy for this request. */ private RetryPolicy mRetryPolicy; /** * When a request can be retrieved from cache but must be refreshed from * the network, the cache entry will be stored here so that in the event of * a "Not Modified" response, we can be sure it hasn"t been evicted from cache. */ private Cache.Entry mCacheEntry = null; /** An opaque token tagging this request; used for bulk cancellation. */ private Object mTag; }
我們簡(jiǎn)單的分下類:
mMethod、mUrl:用什么 Http 方法給誰(shuí)發(fā)請(qǐng)求,mUrl 中可以附帶參數(shù)(如果想在 body 中傳遞參數(shù)呢,想定制 Header 呢)
mShouldCache、mCanceled、mCacheEntry:緩存相關(guān)
mRequestQueue:關(guān)聯(lián)的 RequestQueue,結(jié)束 Request 時(shí)使用
mErrorListener:錯(cuò)誤回調(diào),想想上面的 demo,還有一個(gè)成功的回調(diào),但這里并沒有,下文再說
mShouldRetryServerErrors、mRetryPolicy:控制重試
mTag:不同的 Request 可以設(shè)置相關(guān)的 tag,這樣我們可以用 tag 來批量取消 Request
mSequence、mDefaultTrafficStatsTag:忽略
想一想當(dāng)我們自定義一個(gè) Request 的時(shí)候會(huì)關(guān)心那些屬性呢,Method、Url、Cache、ErrorListener,大概就這樣吧。
在上面的分類中還有一些問題要解決,一個(gè)一個(gè)來。
public byte[] getBody() throws AuthFailureError { Mapparams = getParams(); if (params != null && params.size() > 0) { return encodeParameters(params, getParamsEncoding()); } return null; }
這是個(gè) public 方法,我們可以 override 它。不急,可以看到它使用了 getParams()、encodeParameters()、getParamsEncoding() 方法,我們 override 這三個(gè)方法也可以達(dá)到間接影響 Body 的效果,encodeParameters() 是 private 的,還好一般沒有改變它的必要。
Headerpublic MapgetHeaders() throws AuthFailureError { return Collections.emptyMap(); }
override!
請(qǐng)求成功的回調(diào)abstract protected void deliverResponse(T response);另外一個(gè)比較重要的方法
abstract protected ResponseparseNetworkResponse(NetworkResponse response);
將 NetworkResponse 轉(zhuǎn)化為客戶端的類型,這是一個(gè) abstract 方法,我們自定義的 Request 子類必須實(shí)現(xiàn)。事實(shí)上,demo 中的 StringRequest 就是 volley 中的一個(gè) Request 的具體實(shí)現(xiàn),我們可以從中學(xué)習(xí):
@Override protected ResponseparseNetworkResponse(NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); }
可以看到,Request 定義了一個(gè)網(wǎng)絡(luò)請(qǐng)求的細(xì)節(jié)問題,服務(wù)、參數(shù)、重試策略、緩存策略、返回?cái)?shù)據(jù)處理等。其中 Header、參數(shù)、返回值類型的變化可以通過繼承 Request,實(shí)現(xiàn)不同的子類來實(shí)現(xiàn)。在 volley 中內(nèi)置了一些實(shí)現(xiàn),包括 StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest等,繼承的方式顯得不太靈活,需要定義一堆的 Request。我們可以稍作擴(kuò)展,用組合來避免 Request 類的爆炸。
public interface ResponseParser{ Response parseNetworkResponse(NetworkResponse networkResponse); } public class MyRequest extends Request { final private Map params; final private Map headers; final private ResponseParser responseParser; final private Response.Listener listener; public MyRequest(int method, String url, Response.ErrorListener errorListener, Map params, Map headers, ResponseParser responseParser, Response.Listener listener) { super(method, url, errorListener); this.params = params; this.headers = headers; this.responseParser = responseParser; this.listener = listener; } @Override protected Response parseNetworkResponse(NetworkResponse networkResponse) { return responseParser.parseNetworkResponse(networkResponse); } @Override protected void deliverResponse(T t) { listener.onResponse(t); } @Override public Map getHeaders() throws AuthFailureError { return headers; } @Override protected Map getParams() throws AuthFailureError { return params; } }
在使用 volley 的過程中,我們大部分時(shí)間會(huì)是處理 Request 相關(guān)的內(nèi)容。將 Request 設(shè)計(jì)的簡(jiǎn)單直白,降低使用者的難度,這是 volley 非常友好的地方,同時(shí),Volley 的 Request 創(chuàng)建過程也許并不是太有趣,比如請(qǐng)求參數(shù)的創(chuàng)建,無(wú)聊且不太直觀。根據(jù)具體的業(yè)務(wù),寫一些 Builder 可以一定程度彌補(bǔ)這一點(diǎn)。接下來是 RequestQueue 的部分,RequestQueue 在邏輯上是動(dòng)態(tài)的,是一臺(tái)機(jī)器運(yùn)轉(zhuǎn)的部分,稍微復(fù)雜一些,但是,在某種意義上它又是穩(wěn)定的,它提供了很好的默認(rèn)實(shí)現(xiàn),多數(shù)情況下我們不需要定制,不需要擴(kuò)展,不需要理解具體細(xì)節(jié),它在那里,它完美的 work。
RequestQueueRequestQueue 這臺(tái)機(jī)器是如何運(yùn)轉(zhuǎn)的呢?使用 add 方法,一個(gè) Request 首先會(huì)被放入 mCacheQueue,mCacheQueue 是一個(gè) PriorityBlockingQueue,CacheDispatcher 從 mCacheQueue 中取出 Request,查看 cache 中是否已經(jīng)有相同之前相同的請(qǐng)求得到的有效緩存,有就直接使用 ResponseDelivery 將緩存的結(jié)果分發(fā)到 Request 中的 deliverResponse 方法,從而傳遞到用戶手中;如果沒有有效的緩存結(jié)果,則將 Request 添加到 mNetworkQueue,NetworkDispatcher 從 mNetworkQueue 中讀取 Request,使用 Network 執(zhí)行 Request 中的請(qǐng)求,成功后將結(jié)果放入 Cache,同時(shí)用 ResponseDelivery 分發(fā)結(jié)果。這就是一個(gè) Request 的周期。
流程圖如下:
上面我們提到了一些關(guān)鍵的類或?qū)傩浴?/p>
mCacheQueue、mNetworkQueue:兩個(gè)都是 PriorityBlockingQueue,結(jié)合 Request 實(shí)現(xiàn)的 Comparable 接口,Request 的優(yōu)先級(jí)在這里實(shí)現(xiàn)。
CacheDispatcher、NetworkDispatcher:負(fù)責(zé)從 Request 到 Response 的轉(zhuǎn)化
Cache、Network:將 Request 轉(zhuǎn)化為 Response 的實(shí)際執(zhí)行者
ResponseDelivery:分發(fā) Response
有了這些概念后,我們分別分析這些類的代碼,試著由低向上來理解 RequestQueue。
CacheDispatcherpublic class CacheDispatcher extends Thread { public CacheDispatcher( BlockingQueue> cacheQueue, BlockingQueue > networkQueue, Cache cache, ResponseDelivery delivery) { mCacheQueue = cacheQueue; mNetworkQueue = networkQueue; mCache = cache; mDelivery = delivery; } public void quit() { mQuit = true; interrupt(); } @Override public void run() { while (true) { try { // Get a request from the cache triage queue, blocking until // at least one is available. final Request> request = mCacheQueue.take(); Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { mNetworkQueue.put(request); continue; } Response> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); mDelivery.postResponse(request, response); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } } } }
為便于理解,刪去了大量代碼,只保留下核心部分。
在構(gòu)造方法中,我們看到了 mCacheQueue,Request 在 RequestQueue 類中會(huì)先放到這里;還有mNetworkQueue,mCacheQueue 中的 Request 沒有命中 Cache,會(huì)被再放到這里;mCache,存取 Response 的地方;mDelivery,Response 被分發(fā)的地方。
CacheDispatcher 繼承了 Thread,我們可以 quit 它,看到 quit 中先標(biāo)記,然后 interrupt,意味著 run 方法中會(huì)相應(yīng)的處理。
run 中,在線程中開啟了無(wú)限循環(huán),每次循環(huán)從 mCacheQueue 讀取 Request,然后進(jìn)行 1 中提到的過程。也可以看到對(duì)于 quit 的處理。
NetworkDispatcherpublic class NetworkDispatcher extends Thread { public NetworkDispatcher(BlockingQueue> queue, Network network, Cache cache, ResponseDelivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; mDelivery = delivery; } public void quit() { mQuit = true; interrupt(); } @Override public void run() { while (true) { Request> request; 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; } NetworkResponse networkResponse = mNetwork.performRequest(request); Response> response = request.parseNetworkResponse(networkResponse);. if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); } mDelivery.postResponse(request, response); } } }
NetworkDispatcher 和 CacheDispatcher 是不是非常相似?
Cache一個(gè)接口類,和普通的 Cache 沒有什么太大差別,不贅述。
Network
public interface Network { public NetworkResponse performRequest(Request> request) throws VolleyError; } public interface HttpStack { public HttpResponse performRequest(Request> request, MapadditionalHeaders) throws IOException, AuthFailureError; }
它的實(shí)現(xiàn)是 BasicNetwork,BasicNetwork 依賴 HttpStack,HttpStack 是實(shí)際發(fā)起網(wǎng)絡(luò)請(qǐng)求的地方,volley 分別提供了 HttpClient 和 HttpURLConnection 的實(shí)現(xiàn)。我們可以定制這部分,比如基于 OKHttp 的實(shí)現(xiàn)。在最后看 Volley 類源碼時(shí)我們可以看到這個(gè)過程。
ResponseDeliveryResponseDelivery 負(fù)責(zé)分發(fā) Response,對(duì) Android 來說,就是通過 Handler 將 Response 發(fā)送到了主線程
public class ExecutorDelivery implements ResponseDelivery { private final Executor mResponsePoster; public ExecutorDelivery(final Handler handler) { mResponsePoster = new Executor() { @Override public void execute(Runnable command) { handler.post(command); } }; } @Override public void postResponse(Request> request, Response> response, Runnable runnable) { mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable)); } }
在 ResponseDeliveryRunnable 中調(diào)用了 Request.deliverResponse。
RequestQueue有了上面的內(nèi)容,理解 RequestQueue 就很容易了。首先看構(gòu)造方法。
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; }
一共四個(gè)參數(shù)。
cache:一個(gè)實(shí)現(xiàn)了讀寫刪功能的 Cache 對(duì)象,通過它我們可以實(shí)現(xiàn)自己的緩存策略。
Network:前面提到,這是一個(gè)接口,只有一個(gè)要實(shí)現(xiàn)的方法,傳入一個(gè) Request,傳出一個(gè) Response?;蛟S我們也可以把緩存策略放到這里面來,讓 Cache 對(duì) RequestQueue 透明,也未嘗不是一個(gè)不錯(cuò)的想法。
threadPoolSize:NetworkDispatcher 的個(gè)數(shù),決定了網(wǎng)絡(luò)請(qǐng)求的最大并發(fā)數(shù)。
ResponseDelivery:分發(fā) Response,默認(rèn)實(shí)現(xiàn) ExecutorDelivery 將 Response 分發(fā)到 Request.deliverResponse, 這就是我們 override deliverResponse 方法可以得到 Response 的原因,同時(shí)我們從上面 ExecutorDelivery 的代碼中看到,這個(gè)分發(fā)是通過像一個(gè) Handler post 一個(gè) Runnable 實(shí)現(xiàn)的,Handler 的性質(zhì)決定了數(shù)據(jù)分發(fā)到哪個(gè)線程。
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(); } }
mCacheDispatcher、mDispatchers 初始化,從上面我們知道它們都是 Thread 的子類, start 后內(nèi)部開始循環(huán),從 mCacheQueue、mNetworkQueue 讀取 Request 進(jìn)行處理。
publicRequest 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; } }
和我們開始討論 RequestQueue 時(shí)的流程圖多了一些細(xì)節(jié)的處理,比如如果一個(gè) Request 被設(shè)置為不可緩存,則直接添加到 mNetworkQueue;有相等的 Request 時(shí),會(huì)把重復(fù)的請(qǐng)求加到一個(gè)等待隊(duì)列,分享請(qǐng)求結(jié)果。
RequestQueue 的內(nèi)容就到這里了。RequestQueue 在并發(fā)上沒有選擇使用 Java 提供的線程池,而是使用固定數(shù)據(jù)的線程數(shù)組(NetworkDispatcher)配合阻塞的優(yōu)先級(jí)隊(duì)列來實(shí)現(xiàn)任務(wù)并發(fā)。同時(shí)在緩存、網(wǎng)絡(luò)、分發(fā)方式上提供了可定制的接口,當(dāng)然,多數(shù)時(shí)候使用默認(rèn)實(shí)現(xiàn)就可以了。為了方便的使用默認(rèn)的實(shí)現(xiàn),就像文章開始的 demo 展示的那樣,volley 提供了一個(gè)工具類 Volley。
VolleyVolley 只提供兩個(gè)方法。用來創(chuàng)建默認(rèn)的 RequestQueue,如下:
public class Volley { public static RequestQueue newRequestQueue(Context context, HttpStack stack) { 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 { // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } Network network = new BasicNetwork(stack); RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); queue.start(); return queue; } public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null); } }擴(kuò)展
Volley 自帶的 NetworkImageView
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66937.html
摘要:前言想必很多人都用過,為了建立網(wǎng)絡(luò)編程的知識(shí)體系,是必須要講的知識(shí)點(diǎn),所以我這里有必要再次介紹一下的使用。簡(jiǎn)介在年大會(huì)上推出了一個(gè)新的網(wǎng)絡(luò)通信框架。在使用前請(qǐng)下載庫(kù)并放在目錄下并到工程中。 前言 Volley想必很多人都用過,為了建立網(wǎng)絡(luò)編程的知識(shí)體系,Volley是必須要講的知識(shí)點(diǎn),所以我這里有必要再次介紹一下Volley的使用。 1.Volley簡(jiǎn)介 在2013年Google I/...
摘要:接下來看看網(wǎng)絡(luò)調(diào)度線程。讓我們?cè)倩氐?,?qǐng)求網(wǎng)絡(luò)后,會(huì)將響應(yīng)結(jié)果存在緩存中,如果響應(yīng)結(jié)果成功則調(diào)用來回調(diào)給主線程。我們用請(qǐng)求網(wǎng)絡(luò)的寫法是這樣的將請(qǐng)求添加在請(qǐng)求隊(duì)列中看到第行整個(gè)的大致流程都通了吧,好了關(guān)于的源碼就講到這里。 1.Volley結(jié)構(gòu)圖 showImg(https://segmentfault.com/img/remote/1460000011351317); 從上圖可以看到V...
閱讀 1658·2019-08-30 15:55
閱讀 981·2019-08-30 15:44
閱讀 873·2019-08-30 10:48
閱讀 2045·2019-08-29 13:42
閱讀 3190·2019-08-29 11:16
閱讀 1268·2019-08-29 11:09
閱讀 2060·2019-08-26 11:46
閱讀 620·2019-08-26 11:44