Volley 源码解析之网络请求
Volley源碼分析三部曲
Volley 源碼解析之網(wǎng)絡(luò)請(qǐng)求
Volley 源碼解析之圖片請(qǐng)求
Volley 源碼解析之緩存機(jī)制
Volley 是 Google 推出的一款網(wǎng)絡(luò)通信框架,非常適合數(shù)據(jù)量小、通信頻繁的網(wǎng)絡(luò)請(qǐng)求,支持并發(fā)、緩存和容易擴(kuò)展、調(diào)試等;不過(guò)不太適合下載大文件、大量數(shù)據(jù)的網(wǎng)絡(luò)請(qǐng)求,因?yàn)関olley在解析期間將響應(yīng)放到內(nèi)存中,我們可以使用okhttp或者系統(tǒng)提供的DownloadManager來(lái)下載文件。
一、簡(jiǎn)單使用
首先在工程引入volley的library:
dependencies {implementation 'com.android.volley:volley:1.1.1' } 復(fù)制代碼然后需要我們打開網(wǎng)絡(luò)權(quán)限,我這里直接貼出官網(wǎng)簡(jiǎn)單請(qǐng)求的示例代碼:
final TextView mTextView = (TextView) findViewById(R.id.text); // ...// Instantiate the RequestQueue. RequestQueue queue = Volley.newRequestQueue(this); String url ="http://www.google.com";// Request a string response from the provided URL. StringRequest stringRequest = new StringRequest(Request.Method.GET, url,new Response.Listener<String>() {public void onResponse(String response) {// Display the first 500 characters of the response string.mTextView.setText("Response is: "+ response.substring(0,500));} }, new Response.ErrorListener() {public void onErrorResponse(VolleyError error) {mTextView.setText("That didn't work!");} });// Add the request to the RequestQueue. queue.add(stringRequest); 復(fù)制代碼使用相對(duì)簡(jiǎn)單,回調(diào)直接在主線程,我們?nèi)∠硞€(gè)請(qǐng)求直接這樣操作:
定義一個(gè)標(biāo)記添加到requests中
public static final String TAG = "MyTag"; StringRequest stringRequest; // Assume this exists. RequestQueue mRequestQueue; // Assume this exists.// Set the tag on the request. stringRequest.setTag(TAG);// Add the request to the RequestQueue. mRequestQueue.add(stringRequest); 復(fù)制代碼然后我們可以在 onStop() 中取消所有標(biāo)記的請(qǐng)求
protected void onStop () {super.onStop();if (mRequestQueue != null) {mRequestQueue.cancelAll(TAG);} } 復(fù)制代碼二、源碼分析
我們先從Volley這個(gè)類入手:
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 {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; }public static RequestQueue newRequestQueue(Context context) {return newRequestQueue(context, (BaseHttpStack) null); } 復(fù)制代碼當(dāng)我們傳遞一個(gè)Context的時(shí)候,首先為BaseHttpStack為null,會(huì)執(zhí)行到創(chuàng)建BaseHttpStack,BaseHttpStack是一個(gè)網(wǎng)絡(luò)具體的處理請(qǐng)求,Volley默認(rèn)提供了基于HttpURLCollection的HurlStack和基于HttpClient的HttpClientStack。Android6.0移除了HttpClient,Google官方推薦使用HttpURLCollection類作為替換。所以這里在API大于9的版本是用的是HurlStack,為什么這樣選擇,詳情可見(jiàn)這篇博客Android訪問(wèn)網(wǎng)絡(luò),使用HttpURLConnection還是HttpClient?。我們使用的是默認(rèn)的構(gòu)造,BaseHttpStack傳入為null,如果我們想使用自定義的okhttp替換底層,我們直接繼承HttpStack重寫即可,也可以自定義Network和RequestQueue,Volley的高擴(kuò)展性充分體現(xiàn)。接下來(lái)則創(chuàng)建一個(gè)Network對(duì)象,然后實(shí)例化RequestQueue,首先創(chuàng)建了一個(gè)用于緩存的文件夾,然后創(chuàng)建了一個(gè)磁盤緩存,將文件緩存到指定目錄的硬盤上,默認(rèn)大小是5M,但是大小可以配置。接下來(lái)調(diào)用RequestQueue的start()方法進(jìn)行啟動(dòng),我們進(jìn)入這個(gè)方法查看一下:
public void start() {stop(); mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);mCacheDispatcher.start();for (int i = 0; i < mDispatchers.length; i++) {NetworkDispatcher networkDispatcher =new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);mDispatchers[i] = networkDispatcher;networkDispatcher.start();} } 復(fù)制代碼開始啟動(dòng)的時(shí)候先停止所有的請(qǐng)求線程和網(wǎng)絡(luò)緩存線程,然后實(shí)例化一個(gè)緩存線程并運(yùn)行,然后一個(gè)循環(huán)開啟DEFAULT_NETWORK_THREAD_POOL_SIZE(4)個(gè)網(wǎng)絡(luò)請(qǐng)求線程并運(yùn)行,一共就是5個(gè)線程在后臺(tái)運(yùn)行,不斷的等待網(wǎng)絡(luò)請(qǐng)求的到來(lái)。
構(gòu)造了RequestQueue之后,我們調(diào)用add()方法將相應(yīng)的Request傳入就開始執(zhí)行網(wǎng)絡(luò)請(qǐng)求了,我們看看這個(gè)方法:
public <T> Request<T> add(Request<T> request) {//將請(qǐng)求隊(duì)列和請(qǐng)求關(guān)聯(lián)起來(lái)request.setRequestQueue(this);//添加到正在請(qǐng)求中但是還未完成的集合中synchronized (mCurrentRequests) {mCurrentRequests.add(request);}//設(shè)置請(qǐng)求的一個(gè)序列號(hào),通過(guò)原子變量的incrementAndGet方法,//以原子方式給當(dāng)前值加1并獲取新值實(shí)現(xiàn)請(qǐng)求的優(yōu)先級(jí)request.setSequence(getSequenceNumber());//添加一個(gè)調(diào)試信息request.addMarker("add-to-queue");//如果不需要緩存則直接加到網(wǎng)絡(luò)的請(qǐng)求隊(duì)列,默認(rèn)每一個(gè)請(qǐng)求都是緩存的,//如果不需要緩存需要調(diào)用Request的setShouldCache方法來(lái)修改if (!request.shouldCache()) {mNetworkQueue.add(request);return request;}//加到緩存的請(qǐng)求隊(duì)列mCacheQueue.add(request);return request; } 復(fù)制代碼關(guān)鍵地方都寫了注釋,主要作用就是將請(qǐng)求加到請(qǐng)求隊(duì)列,執(zhí)行網(wǎng)絡(luò)請(qǐng)求或者從緩存中獲取結(jié)果。網(wǎng)絡(luò)和緩存的請(qǐng)求都是一個(gè)優(yōu)先級(jí)阻塞隊(duì)列,按照優(yōu)先級(jí)出隊(duì)。上面幾個(gè)關(guān)鍵步驟,添加到請(qǐng)求集合里面還有設(shè)置優(yōu)先級(jí)以及添加到緩存和請(qǐng)求隊(duì)列都是線程安全的,要么加鎖,要么使用線程安全的隊(duì)列或者原子操作。
接下來(lái)我們看看添加到CacheDispatcher緩存請(qǐng)求隊(duì)列的run方法:
public void run() {if (DEBUG) VolleyLog.v("start new dispatcher");Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//初始化DiskBasedCache的緩存類mCache.initialize();while (true) {try {processRequest();} catch (InterruptedException e) {if (mQuit) {Thread.currentThread().interrupt();return;}VolleyLog.e("Ignoring spurious interrupt of CacheDispatcher thread; "+ "use quit() to terminate it");}} } 復(fù)制代碼接下來(lái)的重點(diǎn)是看看processRequest()這個(gè)方法:
private void processRequest() throws InterruptedException {//從緩存隊(duì)列取出請(qǐng)求final Request<?> request = mCacheQueue.take();processRequest(request); } void processRequest(final Request<?> request) throws InterruptedException {request.addMarker("cache-queue-take");// 如果請(qǐng)求被取消,我們可以通過(guò)RequestQueue的回調(diào)接口來(lái)監(jiān)聽(tīng)if (request.isCanceled()) {request.finish("cache-discard-canceled");return;}// 從緩存中獲取Cache.EntryCache.Entry entry = mCache.get(request.getCacheKey());//沒(méi)有取到緩存if (entry == null) {request.addMarker("cache-miss");// 緩存未命中,對(duì)于可緩存的請(qǐng)求先去檢查是否有相同的請(qǐng)求是否已經(jīng)在運(yùn)行中,//如果有的話先加入請(qǐng)求等待隊(duì)列,等待請(qǐng)求完成,返回true;如果返回false則表示第一次請(qǐng)求if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {//加入到網(wǎng)絡(luò)請(qǐng)求的阻塞隊(duì)列mNetworkQueue.put(request);}return;}// 如果緩存完全過(guò)期,處理過(guò)程跟上面類似if (entry.isExpired()) {request.addMarker("cache-hit-expired");//設(shè)置請(qǐng)求緩存的entry到這個(gè)request中request.setCacheEntry(entry);if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {mNetworkQueue.put(request);}return;}//緩存命中,將數(shù)據(jù)解析并返回到request的抽象方法中request.addMarker("cache-hit");Response<?> response =request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));request.addMarker("cache-hit-parsed");//判斷請(qǐng)求結(jié)果是否需要刷新if (!entry.refreshNeeded()) {// 未過(guò)期的緩存命中,通過(guò)ExecutorDelivery回調(diào)給我們的request子類的接口中,// 我們?cè)谑褂玫臅r(shí)候就可以通過(guò)StringRequest、JsonRequest等拿到結(jié)果,// 切換到主線程也是在這個(gè)類里執(zhí)行的mDelivery.postResponse(request, response);} else {request.addMarker("cache-hit-refresh-needed");request.setCacheEntry(entry);// 將這個(gè)響應(yīng)標(biāo)記為中間值,即這個(gè)響應(yīng)是新鮮的,那么第二個(gè)響應(yīng)正在請(qǐng)求隨時(shí)到來(lái)response.intermediate = true;if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {//發(fā)起網(wǎng)絡(luò)請(qǐng)求,這里為什么直接調(diào)用上面的mNetworkQueue.put(request);呢,//主要是為了添加一個(gè)已經(jīng)分發(fā)的標(biāo)記,在響應(yīng)分發(fā)的時(shí)候不再回調(diào)給用戶,//不然就回調(diào)了兩次mDelivery.postResponse(request,response,new Runnable() {public void run() {try {mNetworkQueue.put(request);} catch (InterruptedException e) {// Restore the interrupted statusThread.currentThread().interrupt();}}});} else {//這里第三個(gè)參數(shù)傳遞null,不用再去分發(fā),因?yàn)橐呀?jīng)有相同的請(qǐng)求已經(jīng)在執(zhí)行,//直接添加到了等待請(qǐng)求的列表中,然后返回的時(shí)候從已經(jīng)執(zhí)行的請(qǐng)求收到響應(yīng)mDelivery.postResponse(request, response);}} } 復(fù)制代碼這部分主要是對(duì)請(qǐng)求的緩存判斷,是否過(guò)期以及需要刷新緩存。我們調(diào)用取消所有請(qǐng)求或者取消某個(gè)請(qǐng)求實(shí)質(zhì)上就是對(duì)mCanceled這個(gè)變量賦值,然后在緩存線程或者網(wǎng)絡(luò)線程里面都回去判斷這個(gè)值,就完成了取消。上面的isExpired和refreshNeeded,兩個(gè)區(qū)別就是,前者如果過(guò)期就直接請(qǐng)求最新的內(nèi)容,后者就是還在新鮮的時(shí)間內(nèi),但是把內(nèi)容返回給用戶還是會(huì)發(fā)起請(qǐng)求,兩者一個(gè)與ttl值相比,另一個(gè)與softTtl相比。
其中有一個(gè)WaitingRequestManager,如果有相同的請(qǐng)求那么就需要一個(gè)暫存的地方,這個(gè)類就是做的這個(gè)操作
private static class WaitingRequestManager implements Request.NetworkRequestCompleteListener {//所有等待請(qǐng)求的集合,鍵是緩存的keyprivate final Map<String, List<Request<?>>> mWaitingRequests = new HashMap<>();private final CacheDispatcher mCacheDispatcher;WaitingRequestManager(CacheDispatcher cacheDispatcher) {mCacheDispatcher = cacheDispatcher;}//請(qǐng)求接受到一個(gè)有效的響應(yīng),后面等待的相同請(qǐng)求就可以使用這個(gè)響應(yīng)public void onResponseReceived(Request<?> request, Response<?> response) {//如果緩存為空或者已經(jīng)過(guò)期,那么就釋放等待的請(qǐng)求if (response.cacheEntry == null || response.cacheEntry.isExpired()) {onNoUsableResponseReceived(request);return;}String cacheKey = request.getCacheKey();//等待的請(qǐng)求的集合List<Request<?>> waitingRequests;synchronized (this) {//從map里面移除這個(gè)請(qǐng)求的集合waitingRequests = mWaitingRequests.remove(cacheKey);}if (waitingRequests != null) {if (VolleyLog.DEBUG) {VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",waitingRequests.size(), cacheKey);}// 里面所有的請(qǐng)求都分發(fā)到相應(yīng)的回調(diào)執(zhí)行,下面會(huì)講解for (Request<?> waiting : waitingRequests) {mCacheDispatcher.mDelivery.postResponse(waiting, response);}}}//沒(méi)有收到相應(yīng),則需要釋放請(qǐng)求public synchronized void onNoUsableResponseReceived(Request<?> request) {String cacheKey = request.getCacheKey();List<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);if (waitingRequests != null && !waitingRequests.isEmpty()) {if (VolleyLog.DEBUG) {VolleyLog.v("%d waiting requests for cacheKey=%s; resend to network",waitingRequests.size(), cacheKey);}//下面這個(gè)請(qǐng)求執(zhí)會(huì)重新執(zhí)行,將這個(gè)移除添加到Request<?> nextInLine = waitingRequests.remove(0);//將剩下的請(qǐng)求放到等待請(qǐng)求的map中mWaitingRequests.put(cacheKey, waitingRequests);//在request里面注冊(cè)一個(gè)回調(diào)接口,因?yàn)橹匦麻_始請(qǐng)求,需要重新注冊(cè)一個(gè)監(jiān)聽(tīng),//后面請(qǐng)求成功失敗以及取消都可以收到回調(diào)nextInLine.setNetworkRequestCompleteListener(this);try {//從上面if判斷方法可以得出:waitingRequests != null && !waitingRequests.isEmpty()//排除了第一次請(qǐng)求失敗、取消的情況,后面的那個(gè)條件則表示這個(gè)等待請(qǐng)求隊(duì)列必須要有一個(gè)請(qǐng)求,//同時(shí)滿足才會(huì)執(zhí)行這里面的代碼,一般只要這里面的請(qǐng)求執(zhí)行成功一次后續(xù)所有的請(qǐng)求都會(huì)被移除,//所以這里對(duì)多個(gè)請(qǐng)求的情況,失敗一次,那么后續(xù)的請(qǐng)求會(huì)繼續(xù)執(zhí)行mCacheDispatcher.mNetworkQueue.put(nextInLine);} catch (InterruptedException iex) {VolleyLog.e("Couldn't add request to queue. %s", iex.toString());// Restore the interrupted status of the calling thread (i.e. NetworkDispatcher)Thread.currentThread().interrupt();// Quit the current CacheDispatcher thread.mCacheDispatcher.quit();}}}//對(duì)于可以緩存的請(qǐng)求,相同緩存的請(qǐng)求已經(jīng)在運(yùn)行中就添加到一個(gè)發(fā)送隊(duì)列,//等待運(yùn)行中的隊(duì)列請(qǐng)求完成,返回true表示已經(jīng)有請(qǐng)求在運(yùn)行,false則是第一次執(zhí)行private synchronized boolean maybeAddToWaitingRequests(Request<?> request) {String cacheKey = request.getCacheKey();// 存在相同的請(qǐng)求則把請(qǐng)求加入到相同緩存鍵的集合中if (mWaitingRequests.containsKey(cacheKey)) {// There is already a request in flight. Queue up.List<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);//如果包含相同的請(qǐng)求但是有可能是第二次請(qǐng)求,前面第一次請(qǐng)求插入null了if (stagedRequests == null) {stagedRequests = new ArrayList<>();}request.addMarker("waiting-for-response");stagedRequests.add(request);mWaitingRequests.put(cacheKey, stagedRequests);if (VolleyLog.DEBUG) {VolleyLog.d("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);}return true;} else {//第一次請(qǐng)求那么則插入一個(gè)null,表示當(dāng)前有一個(gè)請(qǐng)求正在運(yùn)行mWaitingRequests.put(cacheKey, null);//注冊(cè)一個(gè)接口監(jiān)聽(tīng)request.setNetworkRequestCompleteListener(this);if (VolleyLog.DEBUG) {VolleyLog.d("new request, sending to network %s", cacheKey);}return false;}} } 復(fù)制代碼這個(gè)類主要是避免相同的請(qǐng)求多次請(qǐng)求,而且在NetworkDispatcher里面也會(huì)通過(guò)這個(gè)接口回調(diào)相應(yīng)的值在這里執(zhí)行,最終比如在網(wǎng)絡(luò)請(qǐng)求返回304、請(qǐng)求取消或者異常那么都會(huì)在這里來(lái)處理,如果收到響應(yīng)則會(huì)把值回調(diào)給用戶,后面的請(qǐng)求也不會(huì)再去請(qǐng)求,如果無(wú)效的響應(yīng)則會(huì)做一些釋放等待的請(qǐng)求操作,請(qǐng)求完成也會(huì)將后面相同的請(qǐng)求回調(diào)給用戶,三個(gè)方法都在不同的地方發(fā)揮作用。
我們接下來(lái)看看NetworkDispatcher網(wǎng)絡(luò)請(qǐng)求隊(duì)列的run方法中的processRequest方法:
void processRequest(Request<?> request) {long startTimeMs = SystemClock.elapsedRealtime();try {request.addMarker("network-queue-take");// 請(qǐng)求被取消了,就不執(zhí)行網(wǎng)絡(luò)請(qǐng)求,if (request.isCanceled()) {request.finish("network-discard-cancelled");request.notifyListenerResponseNotUsable();return;}addTrafficStatsTag(request);// 這里就是執(zhí)行網(wǎng)絡(luò)請(qǐng)求的地方NetworkResponse networkResponse = mNetwork.performRequest(request);request.addMarker("network-http-complete");// 如果服務(wù)器返回304響應(yīng),即沒(méi)有修改過(guò),//緩存依然是有效的并且是在需要刷新的有效期內(nèi),那么則不需要解析響應(yīng)if (networkResponse.notModified && request.hasHadResponseDelivered()) {request.finish("not-modified");//沒(méi)有收到來(lái)自網(wǎng)絡(luò)的有效響應(yīng),釋放請(qǐng)求request.notifyListenerResponseNotUsable();return;}// 在工作線程中解析這些響應(yīng)Response<?> response = request.parseNetworkResponse(networkResponse);request.addMarker("network-parse-complete");// 將緩存寫入到應(yīng)用if (request.shouldCache() && response.cacheEntry != null) {mCache.put(request.getCacheKey(), response.cacheEntry);request.addMarker("network-cache-written");}// 標(biāo)記此請(qǐng)求已將分發(fā)request.markDelivered();//將請(qǐng)求的響應(yīng)回調(diào)給用戶mDelivery.postResponse(request, response);//請(qǐng)求接受到了一個(gè)響應(yīng),其他相同的請(qǐng)求可以使用這個(gè)響應(yīng)request.notifyListenerResponseReceived(response);} catch (VolleyError volleyError) {...} } 復(fù)制代碼這里才是網(wǎng)絡(luò)請(qǐng)求的真正執(zhí)行以及解析分發(fā)的地方,重點(diǎn)看兩個(gè)地方的代碼,執(zhí)行和解析,我們先看看執(zhí)行網(wǎng)絡(luò)請(qǐng)求這個(gè)代碼,執(zhí)行的地方是BasicNetwork.performRequest,下面看看這個(gè)方法:
public NetworkResponse performRequest(Request<?> request) throws VolleyError {long requestStart = SystemClock.elapsedRealtime();while (true) {HttpResponse httpResponse = null;byte[] responseContents = null;List<Header> responseHeaders = Collections.emptyList();try {// 構(gòu)造緩存的頭部,添加If-None-Match和If-Modified-Since,都是http/1.1中控制協(xié)商緩存的兩個(gè)字段, // If-None-Match:客服端再次發(fā)起請(qǐng)求時(shí),攜帶上次請(qǐng)求返回的唯一標(biāo)識(shí)Etag值,//服務(wù)端用攜帶的值和最后修改的值作對(duì)比,最后修改時(shí)間大于攜帶的字段值則返回200,否則304;// If-Modified-Since:客服端再次發(fā)起請(qǐng)求時(shí),攜帶上次請(qǐng)求返回的Last-Modified值,//服務(wù)端用攜帶的值和服務(wù)器的Etag值作對(duì)比,一致則返回304Map<String, String> additionalRequestHeaders =getCacheHeaders(request.getCacheEntry());//因?yàn)楝F(xiàn)在一般的sdk都是大于9的,那么這里執(zhí)行的就是HurlStack的executeRequest方法,//執(zhí)行網(wǎng)絡(luò)請(qǐng)求,和我們平時(shí)使用HttpURLConnection請(qǐng)求網(wǎng)絡(luò)大致相同httpResponse = mBaseHttpStack.executeRequest(request, additionalRequestHeaders);int statusCode = httpResponse.getStatusCode();responseHeaders = httpResponse.getHeaders();// 服務(wù)端返回304時(shí),那么就表示資源無(wú)更新,可以繼續(xù)使用緩存的值if (statusCode == HttpURLConnection.HTTP_NOT_MODIFIED) {Entry entry = request.getCacheEntry();if (entry == null) {return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED,/* data= */ null,/* notModified= */ true,SystemClock.elapsedRealtime() - requestStart,responseHeaders);}// 將緩存頭和響應(yīng)頭組合在一起,一次響應(yīng)就完成了List<Header> combinedHeaders = combineHeaders(responseHeaders, entry);return new NetworkResponse(HttpURLConnection.HTTP_NOT_MODIFIED,entry.data,/* notModified= */ true,SystemClock.elapsedRealtime() - requestStart,combinedHeaders);}// 如果返回204,執(zhí)行成功,沒(méi)有數(shù)據(jù),這里需要檢查InputStream inputStream = httpResponse.getContent();if (inputStream != null) {responseContents =inputStreamToBytes(inputStream, httpResponse.getContentLength());} else {//返回204,就返回一個(gè)空的byte數(shù)組responseContents = new byte[0];}// if the request is slow, log it.long requestLifetime = SystemClock.elapsedRealtime() - requestStart;logSlowRequests(requestLifetime, request, responseContents, statusCode);if (statusCode < 200 || statusCode > 299) {throw new IOException();}return new NetworkResponse(statusCode,responseContents,/* notModified= */ false,SystemClock.elapsedRealtime() - requestStart,responseHeaders);} catch (SocketTimeoutException e) {//異常進(jìn)行重新請(qǐng)求等...}} } 復(fù)制代碼這里主要執(zhí)行了添加緩存頭并發(fā)起網(wǎng)絡(luò)請(qǐng)求,然后將返回值組裝成一個(gè)NetworkResponse值返回,接下來(lái)我們看看是如何解析這個(gè)值的,解析是由Request的子類去實(shí)現(xiàn)的,我們就看系統(tǒng)提供的StringRequest:
("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)); } 復(fù)制代碼我們可以看到將值組裝成一個(gè)String,然后組裝成一個(gè)Response返回,接下來(lái)看看這里如何將這個(gè)值回調(diào)給用戶的這個(gè)方法mDelivery.postResponse(request, response),這里我們先重點(diǎn)看看這個(gè)類ExecutorDelivery:
public class ExecutorDelivery implements ResponseDelivery {//構(gòu)造執(zhí)行已提交的Runnable任務(wù)對(duì)象private final Executor mResponsePoster;//這里在RequestQueue構(gòu)造參數(shù)中初始化,new ExecutorDelivery(new Handler(Looper.getMainLooper())),//那么這里runnable就通過(guò)綁定主線程的Looper的Handler對(duì)象投遞到主線程中執(zhí)行public ExecutorDelivery(final Handler handler) {// Make an Executor that just wraps the handler.mResponsePoster =new Executor() {public void execute(Runnable command) {handler.post(command);}};}public ExecutorDelivery(Executor executor) {mResponsePoster = executor;}//這個(gè)方法就是我們NetworkDispatcher里面調(diào)用的方法,調(diào)用下面這個(gè)三個(gè)參數(shù)的構(gòu)造方法public void postResponse(Request<?> request, Response<?> response) {postResponse(request, response, null);}public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {request.markDelivered();request.addMarker("post-response");//構(gòu)造了一個(gè)ResponseDeliveryRunnable類,傳入execute,現(xiàn)在這個(gè)runnable就是在主線程里執(zhí)行mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));}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. */("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;}("unchecked")public void run() {//請(qǐng)求取消,那么就不分發(fā)給用戶if (mRequest.isCanceled()) {mRequest.finish("canceled-at-delivery");return;}// 根據(jù)isSuccess這個(gè)值來(lái)提供相應(yīng)的回調(diào)給用戶,調(diào)用Response會(huì)通過(guò)error的值是否為null來(lái)確定這個(gè)值,//我們調(diào)用VolleyError這個(gè)構(gòu)造函數(shù)的時(shí)候就為這個(gè)值就為falseif (mResponse.isSuccess()) {mRequest.deliverResponse(mResponse.result);} else {mRequest.deliverError(mResponse.error);}// 如果這是一個(gè)在新鮮的時(shí)間內(nèi)的請(qǐng)求的響應(yīng),就添加一個(gè)標(biāo)記,否則就結(jié)束if (mResponse.intermediate) {mRequest.addMarker("intermediate-response");} else {mRequest.finish("done");}// 在CacheDispatcher里面當(dāng)請(qǐng)求第一次請(qǐng)求時(shí)直接調(diào)用三個(gè)參數(shù)的構(gòu)造方法,通過(guò)這個(gè)runnable就執(zhí)行run方法if (mRunnable != null) {mRunnable.run();}}} }復(fù)制代碼上面方法主要是將值回調(diào)給用戶,那么整個(gè)網(wǎng)絡(luò)請(qǐng)求大致就完成了,其中還涉及很多細(xì)節(jié)的東西,但是大致流程是走通了,不得不說(shuō)這個(gè)庫(kù)有很多值得我們學(xué)習(xí)的地方。
三、總結(jié)
現(xiàn)在我們看官網(wǎng)的一張圖,總結(jié)一下整個(gè)流程:
- 藍(lán)色是主線程
- 綠色是緩存線程
- 黃色是網(wǎng)絡(luò)線程
我們可以看到首先是請(qǐng)求添加到RequestQueue里,首先是添加到緩存隊(duì)列,然后查看是否已經(jīng)緩存,如果有并且在有效期內(nèi)的緩存直接回調(diào)給用戶,如果沒(méi)有查找到,那么則需要添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列重新請(qǐng)求并且解析響應(yīng)、寫入緩存在發(fā)送到主線程給用戶回調(diào)。
參考以及相關(guān)鏈接
- 【第1250期】徹底理解瀏覽器的緩存機(jī)制
- Android Volley完全解析(四),帶你從源碼的角度理解Volley
- Volley 源碼解析
- Volley 源碼解析
轉(zhuǎn)載于:https://juejin.im/post/5c1c58b35188251f1f320e70
總結(jié)
以上是生活随笔為你收集整理的Volley 源码解析之网络请求的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [MySQL] INFORMATION_
- 下一篇: 深入浅出:HTTP/2