Volley源碼解析之---一次完整的StringRequest請求(1)

wuliVolley請求框架出來已經(jīng)許久許久单料,小白的我埋凯,以前只知其一,而不知其二扫尖。 現(xiàn)在才開始學(xué)習(xí)他的源碼白对,慚愧慚愧,既然都落后了换怖,當(dāng)然得乘勝追擊學(xué)起來甩恼,否則只能永遠(yuǎn)落后了(放了一波毒雞湯)。希望能學(xué)習(xí)其中的編程思想以及了解它是如何完成整個(gè)請求以及實(shí)現(xiàn)磁盤緩存的。本文我將通過一個(gè)分析StringRequest的請求來完整的剖析一下其中涉及到的重要代碼条摸。我的初衷一直都沒有變悦污,那就是每寫一篇文章是為了給自己的知識(shí)庫添上一點(diǎn),也是讓自己堅(jiān)持學(xué)習(xí)钉蒲,同時(shí)給學(xué)習(xí)路上的你我他提供一點(diǎn)參考的資料切端。 本文將會(huì)有點(diǎn)長,別失去耐心顷啼,好好看下去踏枣。我相信肯定能讓你對Volley更了解。 Are you Ready钙蒙? Go!

要學(xué)習(xí)一個(gè)源碼茵瀑,我們可以首先從我們使用的地方出發(fā)去學(xué)習(xí),這樣更好理解躬厌,也不至于一下子迷失在源碼的汪洋大海中瘾婿。所以我們先看看我們怎么用它。當(dāng)請求一個(gè)string的時(shí)候烤咧,我們會(huì):

// 1.new一個(gè)RequestQueue
RequestQueue  mRequestQueue = Volley.newRequestQueue(context); 
//2.new一個(gè)StringRequest對象偏陪,傳入Response.Listener 和Response.ErrorListener來監(jiān)聽請求成功和請求失敗的時(shí)候的回調(diào)
StringRequest request = new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
                 @Override
                 public void onResponse(String s) {
                     txt_response.setText(s);
                 }
             }, new Response.ErrorListener() {
                 @Override
                 public void onErrorResponse(VolleyError volleyError) {
                     Log.e("onError", volleyError.getMessage());
                 }
             });
 //將請求加入到請求隊(duì)列中
mRequstQueue.add(request);

RequestQueue正如它的字面意思一樣就是一個(gè)請求隊(duì)列。每當(dāng)有一個(gè)新的請求的時(shí)候就將請求添加進(jìn)去煮嫌。讓我們看看Volley.newRequstQueue做了些什么笛谦。

 public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
    }

可以看到它調(diào)用了另外一個(gè)方法,傳入了一個(gè)null的HttpStack昌阿,這個(gè)是什么呢饥脑?
Volley提供了HttpStack的兩個(gè)實(shí)現(xiàn)子類,HttpClientStack 與 HurlStack HttpClientStack 和HurlStack是兩種網(wǎng)絡(luò)請求的封裝類.當(dāng)Android的SDK版本號(hào)小于9的時(shí)候懦冰,使用的是HttpStack來進(jìn)行網(wǎng)絡(luò)請求灶轰,而HttpClientStack是通過HttpClient來請求網(wǎng)絡(luò)數(shù)據(jù)的。相反刷钢,當(dāng)sdk大于9時(shí)笋颤,使用HurlStack來請求網(wǎng)絡(luò)數(shù)據(jù),HurlStack是通過HttpURLConnection來進(jìn)行網(wǎng)絡(luò)連接并請求數(shù)據(jù)的内地。具體是如何請求數(shù)據(jù)的這里我們就不講解了伴澄,簡單提一下就是通過HttpClient或者HttpUrlConnection 來請求數(shù)據(jù)獲得Response. 感興趣的可以自己在源碼中找到對應(yīng)的類查看一下。
接著往下看:

     /**
     * 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.
     */
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        //定義一個(gè)緩存目錄阱缓,默認(rèn)是在應(yīng)用的緩存目錄下創(chuàng)建一個(gè)叫volley的目錄用于存儲(chǔ)緩存
        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) {
            //如果stack是null非凌,則根據(jù)SDK的版本來創(chuàng)建對應(yīng)的HttpStack的子類
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack(); //通過HttpUrlConnection請求
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                //通過HttpClient連接請求網(wǎng)絡(luò)數(shù)據(jù)
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //傳入HttpStack,創(chuàng)建一個(gè)Network對象荆针,在我看來Network類是對HttpStack的進(jìn)一步封裝敞嗡,添加了對請求結(jié)果的一些處理颁糟,實(shí)際網(wǎng)絡(luò)請求是通過HttpStack實(shí)現(xiàn)的。
        Network network = new BasicNetwork(stack);
        //創(chuàng)建并啟動(dòng)一個(gè)RequestQueue 
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

我已經(jīng)在代碼中添加了對應(yīng)的注釋喉悴,主要就是創(chuàng)建了HttpStack對象傳遞給Network的構(gòu)造函數(shù)棱貌,然后用于網(wǎng)絡(luò)請求,之后通過Network以及DiskBasedCache構(gòu)造一個(gè)RequestQueuestart粥惧。在這里DiskBasedCache是一個(gè)默認(rèn)的管理磁盤緩存的一個(gè)類,用于緩存請求的結(jié)果最盅。Volley的緩存機(jī)制就是通過這個(gè)實(shí)現(xiàn)的突雪,****需要注意的是因?yàn)閂olley的緩存都是緩存在磁盤里面的所以緩存讀取速度要比內(nèi)存緩存更慢****。當(dāng)然你也可以改成自己要的內(nèi)存緩存涡贱,這也是我們學(xué)習(xí)源碼的一個(gè)目的咏删,不僅知道別人是怎么寫的旺矾,更重要的是能夠靈活運(yùn)用并且將其改造成我們需要的樣子榄檬。

接下來我們來看一下RequestQueue的構(gòu)造函數(shù)和start都做了些什么,為什么start之后嚣镜,然后只需要將Request加入隊(duì)列就開始請求數(shù)據(jù)并且得到對應(yīng)的Response激挪。

  通過追蹤代碼辰狡,可以知道最終調(diào)用了這個(gè)構(gòu)造函數(shù)。
  /**
  創(chuàng)建一個(gè)工作池垄分,只有start方法調(diào)用了之后宛篇,進(jìn)程才會(huì)開始
  * 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;
    }

在上面的英文注釋當(dāng)中已經(jīng)說得很清楚了,Cache就是用于將Http請求的Response存儲(chǔ)到磁盤薄湿,Network用于執(zhí)行Http請求叫倍。NetworkDispatcher是一個(gè)線程,用于網(wǎng)絡(luò)請求的分發(fā)豺瘤。這里我們默認(rèn)是開啟了4個(gè)NetworkDispatcher.ResponseDelivery是用于分發(fā)請求的錯(cuò)誤和響應(yīng)的吆倦。
看完了構(gòu)造函數(shù)我們就來看看start()方法吧。

 /**
     * Starts the dispatchers in this queue.
     */
    public void start() {
        //首先將確保所有CacheDispatcher和NetworkDispatcher都停止
        stop();  // Make sure any currently running dispatchers are stopped.
        //接下來開啟CacheDispatcher和NetworkDispatcher線程
        // 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();
        }
    }

這里面的代碼雖然不多坐求,但是確是萬物之源蚕泽。 因?yàn)樗_啟了CacheDispatcherNetworkDispatcher線程,然后開始了監(jiān)聽網(wǎng)絡(luò)請求的工作桥嗤。那么Cache DispatcherNetworkDispatcher分別做了哪些工作呢赛糟。
這里我們先來看看NetworkDispatcher,上面我們說到過它是一個(gè)線程,所以我們先看看它的構(gòu)造方法砸逊,然后直擊他的run方法璧南。

 /**
     * 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;
    }

從上面可以知道,NetworkDispathcer擁有一個(gè)BlockingQueue阻塞式隊(duì)列的一個(gè)引用师逸。用于存儲(chǔ)請求司倚,還有Network, cache, ResponseDelivery這幾個(gè)對象的作用,我在上面有提到過,不記得了可以向上滑動(dòng)一下回憶一下动知。接下來我們看看NetworkDispatcher的run方法皿伺。

    @Override
    public void run() {
        //設(shè)置線程的優(yōu)先級為后臺(tái)線程
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            //獲得開機(jī)到現(xiàn)在的時(shí)間
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // Take a request from the queue.
                //從隊(duì)列中取出一個(gè)請求,如果沒有請求盒粮,就阻塞線程等待請求鸵鸥。
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

                // If the request was cancelled already, do not perform the
                // network request.判斷請求是否被取消了,取消了的話直接finish丹皱,下一次請求處理
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                // Perform the network request. 調(diào)用mNetwork的performRequest妒穴,請求數(shù)據(jù)
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");
                 
                //對返回的NetworkResponse進(jìn)行檢測,如果notModified并且已經(jīng)將結(jié)果分發(fā)過了(返回給用戶了)摊崭,直接finish
                // 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");
                    continue;
                }

                // Parse the response here on the worker thread.
                //將請求轉(zhuǎn)換成Response對象讼油,parseNetworkResponse時(shí)對應(yīng)的Request的子類實(shí)現(xiàn)的
                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) {
                    //如果需要緩存并且cacheEntry不等于空就將本次請求,添加到緩存中
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // Post the response back. 標(biāo)記已經(jīng)將Response分發(fā)給Delivery(即回調(diào)給Request了)
                request.markDelivered();
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                //如果出錯(cuò)了則回調(diào)onError方法
                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);
            }
        }
    }

在代碼中呢簸,我已經(jīng)在需要注釋的地方矮台,添加了詳細(xì)的注釋了。所以我們講一下大概的根时,NetworkDispatcherrun方法在一個(gè)無限循環(huán)中不斷的從請求隊(duì)列中獲取Request瘦赫,然后調(diào)用mNetwork.performRequest請求網(wǎng)絡(luò)數(shù)據(jù)。這里的mNetwork是一個(gè)BasicNetwork對象蛤迎,那么我們就插播一個(gè)BasicNetwork的源碼分析耸彪,從而看看mNetwork.performRequest(request)究竟都干了什么。

BasicNetwork

這部分主要是對BasicNetwork的源碼的解析忘苛。那BasicNetwork是用來干什么的呢蝉娜,BasicNetwork在我的理解中就是通過HttpStack來執(zhí)行一個(gè)Volley request的。我們挑重點(diǎn)的代碼來講解一下扎唾,BasicNetwork主要干了些什么召川。

首先我們看BasicNetwork的定義和構(gòu)造函數(shù):

 
 /**
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
/**
  * @param httpStack HTTP stack to be used
  */
 public BasicNetwork(HttpStack httpStack) {
     // If a pool isn't passed in, then build a small default pool that will give us a lot of
     // benefit and not use too much memory.
     this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
 }

 /**
  * @param httpStack HTTP stack to be used
  * @param pool a buffer pool that improves GC performance in copy operations
  */
 public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
     mHttpStack = httpStack;
     mPool = pool;
 }  

讓我一起來看一下,BasicNetwork繼承自Network,那么Network里面定義了什么呢胸遇?


/**
* An interface for performing requests.
*/
public interface Network {
  /**
   * Performs the specified request.
   * @param request Request to process
   * @return A {@link NetworkResponse} with data and caching metadata; will never be null
   * @throws VolleyError on errors
   */
  NetworkResponse performRequest(Request<?> request) throws VolleyError;
}

可以看到Network只是定義了一個(gè)抽象方法提供給子類實(shí)現(xiàn)荧呐,從方法的注釋來看performRequest就是執(zhí)行了一個(gè)請求并且返回NetworkResponse即網(wǎng)絡(luò)請求的響應(yīng),具體怎么請求的由其子類的實(shí)現(xiàn)決定纸镊。好倍阐,從這里我們就可以知道performRequest是我們請求網(wǎng)絡(luò)最關(guān)鍵的一個(gè)方法。
繼續(xù)看BasicNetwork的構(gòu)造方法逗威,傳入了兩個(gè)參數(shù)峰搪,第一個(gè)參數(shù)HttpStack是一個(gè)封裝了網(wǎng)絡(luò)請求的類,其實(shí)BasicNetwork的網(wǎng)絡(luò)請求的真正的執(zhí)行是調(diào)用了HttpStack來實(shí)現(xiàn)的凯旭,在上一小節(jié)我們提到過概耻。
第二個(gè)參數(shù)是一個(gè)ByteArrayPool,這個(gè)類是android提供的用于避免重復(fù)創(chuàng)建byte[]的一個(gè)緩存機(jī)制使套,類似于LruCache。減少堆內(nèi)存的消耗鞠柄,提高性能侦高。

PerformRqeust

接下來就來看看BasicNetworkperformRequest方法是如何請求網(wǎng)絡(luò)數(shù)據(jù)并且返回響應(yīng),以及如何處理的厌杜。

 @Override
   public NetworkResponse performRequest(Request<?> request) throws VolleyError {
       //獲取開機(jī)到現(xiàn)在的時(shí)間
       long requestStart = SystemClock.elapsedRealtime();
       while (true) {
           HttpResponse httpResponse = null;
           byte[] responseContents = null;
           //創(chuàng)建一個(gè)新的空的集合
           Map<String, String> responseHeaders = Collections.emptyMap();
           try {
               // Gather headers.
               Map<String, String> headers = new HashMap<String, String>();
               //根據(jù)緩存來設(shè)置請求頭的一些值 request.getCacheEntry() 返回一個(gè)request的緩存實(shí)體
               addCacheHeaders(headers, request.getCacheEntry());
               //發(fā)送一個(gè)請求并且得到相應(yīng)的HttpResponse
               httpResponse = mHttpStack.performRequest(request, headers);
               StatusLine statusLine = httpResponse.getStatusLine();
               int statusCode = statusLine.getStatusCode(); //獲取返回的狀態(tài)碼
               //獲取響應(yīng)的頭部信息
               responseHeaders = convertHeaders(httpResponse.getAllHeaders());
               // Handle cache validation.
               if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                   //當(dāng)一個(gè)網(wǎng)絡(luò)請求返回的響應(yīng)碼是304的時(shí)候奉呛,說明該url對應(yīng)的資源沒有改變,所以可以直接從緩存中獲取上次緩存的數(shù)據(jù)
                   Entry entry = request.getCacheEntry();
                   if (entry == null) {
                       //如果entry為空的話夯尽,直接將響應(yīng)頭傳入NetworkResponse
                       return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                               responseHeaders, true,
                               SystemClock.elapsedRealtime() - requestStart);
                   }
                   //當(dāng)響應(yīng)碼是304時(shí)瞧壮,服務(wù)器的響應(yīng)頭里面會(huì)有一些新的信息,所以需要更新緩存中的響應(yīng)頭信息呐萌,比如新的過期時(shí)間等
                   // A HTTP 304 response does not have all header fields. We
                   // have to use the header fields from the cache entry plus
                   // the new ones from the response.
                   // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                   entry.responseHeaders.putAll(responseHeaders);
                   return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                           entry.responseHeaders, true,
                           SystemClock.elapsedRealtime() - requestStart);
               }

               // Some responses such as 204s do not have content.  We must check.
               if (httpResponse.getEntity() != null) {
                   //將HttpEntity轉(zhuǎn)換成對應(yīng)的byte[]數(shù)組
                 responseContents = entityToBytes(httpResponse.getEntity());
               } else {
                 // Add 0 byte response as a way of honestly representing a
                 // no-content request.
                 responseContents = new byte[0];
               }

               // if the request is slow, log it.
               long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
               logSlowRequests(requestLifetime, request, responseContents, statusLine);

               if (statusCode < 200 || statusCode > 299) {
                   throw new IOException();
               }
               return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                       SystemClock.elapsedRealtime() - requestStart);
           } catch (SocketTimeoutException e) {
               attemptRetryOnException("socket", request, new TimeoutError());
           } catch (ConnectTimeoutException e) {
               attemptRetryOnException("connection", request, new TimeoutError());
           } catch (MalformedURLException e) {
               throw new RuntimeException("Bad URL " + request.getUrl(), e);
           } catch (IOException e) {
               int statusCode;
               if (httpResponse != null) {
                   statusCode = httpResponse.getStatusLine().getStatusCode();
               } else {
                   throw new NoConnectionError(e);
               }
               VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
               NetworkResponse networkResponse;
               if (responseContents != null) {
                   networkResponse = new NetworkResponse(statusCode, responseContents,
                           responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                   if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                           statusCode == HttpStatus.SC_FORBIDDEN) {
                       attemptRetryOnException("auth",
                               request, new AuthFailureError(networkResponse));
                   } else if (statusCode >= 400 && statusCode <= 499) {
                       // Don't retry other client errors.
                       throw new ClientError(networkResponse);
                   } else if (statusCode >= 500 && statusCode <= 599) {
                       if (request.shouldRetryServerErrors()) {
                           attemptRetryOnException("server",
                                   request, new ServerError(networkResponse));
                       } else {
                           throw new ServerError(networkResponse);
                       }
                   } else {
                       // 3xx? No reason to retry.
                       throw new ServerError(networkResponse);
                   }
               } else {
                   attemptRetryOnException("network", request, new NetworkError());
               }
           }
       }
   }

在上面的代碼中我自己添加了比較詳細(xì)的注釋了馁痴。我們兩一起看看谊娇,我覺得比較重要的幾個(gè)方法肺孤。 在我們請求之前我們調(diào)用了addCacheHeaders(headers, request.getCacheEntry());來給請求頭添加了一些參數(shù)。 跳進(jìn)addCacheHeaders方法來看看它做了什么!!

 private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
       // If there's no cache entry, we're done.
       if (entry == null) {
           return;
       }
       //初始化"If-None-Match"以及"If-Modified-Since"兩個(gè)變量可以用于之后 判斷是否服務(wù)器上的數(shù)據(jù)是否改變了
       if (entry.etag != null) {
           headers.put("If-None-Match", entry.etag);
       }

       if (entry.lastModified > 0) {
           Date refTime = new Date(entry.lastModified);
           headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
       }
   }

方法很簡單首先判斷一下緩存是否是空的济欢,空的直接返回赠堵,不是空的話,取出entry.etagentry.lastModified分別賦值給請求頭的If-None-MatchIf-Modified-Since.為什么要這樣做呢法褥,在網(wǎng)絡(luò)請求的時(shí)候茫叭,一般當(dāng)我們的請求有緩存的時(shí)候并且我們本地緩存過期的時(shí)候,因?yàn)榫彺嫫谶^了半等,并不代表服務(wù)器上面的資源就一定變了揍愁。所以我們需要判斷一下服務(wù)器上面的資源是否變了。那么如何判斷呢杀饵?
這個(gè)時(shí)候就通過在請求中添加上一次緩存響應(yīng)中的響應(yīng)頭的中的If-Modified-SinceIf-None-Match到請求響應(yīng)頭中莽囤,并傳遞給服務(wù)器。服務(wù)器接收到這個(gè)請求頭的時(shí)候就會(huì)根據(jù)這兩個(gè)變量判斷一下資源是否改變切距。
那么If-Modified-SinceIf-None-Match分別代表了什么呢朽缎。
Last-Modified標(biāo)記了資源文件在服務(wù)器的最后修改時(shí)間,當(dāng)客戶端由于緩存過期發(fā)起請求時(shí)谜悟,請求頭要使用If-Modified-Since頭部话肖,它的值就是第一次服務(wù)器返回的Last-Modified。服務(wù)器收到這個(gè)時(shí)間后葡幸,跟當(dāng)前的資源文件最后修改時(shí)間進(jìn)行對比最筒,如果服務(wù)器中資源文件的最后修改時(shí)間新與Last-Modified的值,那么說明資源文件進(jìn)行修改過蔚叨。
Etag又是什么呢是钥,它是一個(gè)資源文件的標(biāo)示掠归,同樣的當(dāng)客戶端由于緩存過期發(fā)送請求時(shí), 請求頭把上一次服務(wù)器返回的Etag傳入請求頭悄泥,服務(wù)器接收到之后虏冻,與當(dāng)前資源的Etag對比,如果不一樣了弹囚,則說明資源改變了厨相。所以通過這兩個(gè)變量就可以判斷服務(wù)器的資源到底有沒有變。
服務(wù)器通過上面兩個(gè)變量判斷資源是否改變了鸥鹉。如果沒有改變則返回304蛮穿,并返回新的響應(yīng)頭里面包含新的過期時(shí)間。如果改變了則返回200響應(yīng)碼.
addCacheHeaders之后就可以執(zhí)行請求了毁渗,通過httpStack獲得HttpResponse践磅,根據(jù)返回響應(yīng)碼,做相應(yīng)的處理灸异。
如果StatusCode是304府适,則說明服務(wù)器上面的資源沒有改變,則直接從緩存中拿數(shù)據(jù)肺樟,如果entry為空則直接返回一個(gè)data為null檐春,headers==Response的headers的NetworkResponse。 但是如果entry不是空的話么伯,則需要更新entryheaders更新過期時(shí)間等疟暖。
如果返回的響應(yīng)碼statusCode > 200 && statusCode < 299,則 將HttpEntity轉(zhuǎn)換成bytes數(shù)組作為響應(yīng)內(nèi)容,并直接返回NetworkResponse田柔,否則拋出IOException然后做相應(yīng)的處理俐巴,最終的結(jié)果要么返回NetworkRespons,要么拋出VolleyError類型的異常硬爆,然后調(diào)用出根據(jù)返回值欣舵,來回調(diào)對應(yīng)listener的方法。接下來我就不繼續(xù)講了摆屯。相信聰明的你們都能把剩下的看懂的邻遏。_!虐骑!
接著我們就返回到NetworkDispatcher的run方法繼續(xù)向下看准验,現(xiàn)在我們知道mNetwork.performRequest(request)這句話都干了些什么,雖然代碼有點(diǎn)多廷没,但是其實(shí)就是獲得了一個(gè)NetworkResponse糊饱。嚇往下看。為了不用來回拉上去看颠黎,我把剩下的代碼再貼一次另锋。
```
//對返回的NetworkResponse進(jìn)行檢測滞项,如果notModified并且已經(jīng)將結(jié)果分發(fā)過了(返回給用戶了),直接finish
// 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");
continue;
}

            // Parse the response here on the worker thread.
            //將請求轉(zhuǎn)換成Response對象夭坪,parseNetworkResponse時(shí)對應(yīng)的Request的子類實(shí)現(xiàn)的
            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) {
                //如果需要緩存并且cacheEntry不等于空就將本次請求文判,添加到緩存中
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }

            // Post the response back. 標(biāo)記已經(jīng)將Response分發(fā)給Delivery(即回調(diào)給Request了)
            request.markDelivered();
            mDelivery.postResponse(request, response);
        } catch (VolleyError volleyError) {
            //如果出錯(cuò)了則回調(diào)onError方法
            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);
        }
 ```

得到了NetwokResponse之后,我們需要對返回的請求結(jié)果檢測和處理之后室梅,傳遞給mDelivery戏仓,最后反饋掉調(diào)用的請求的Listener中。
首先判斷一下networkResponse.notModified是否為true亡鼠,如果為true赏殃,則說明服務(wù)器上的資源沒有改變,并且request.hasHadResponseDelivered()這個(gè)請求的請求結(jié)果已經(jīng)分發(fā)過了间涵,那么就將這個(gè)請求finish掉仁热,進(jìn)行下一次請求。上面兩個(gè)條件不同時(shí)符合勾哩,那么調(diào)用request.parseNetworkResponse(networkResponse);NetworkResonse轉(zhuǎn)換成Response那么parseNetworkResponse這里方法對Response做了什么呢抗蠢。看看Request的代碼我們可以知道钳幅,parseNetworkResponse是一個(gè)由子類繼承實(shí)現(xiàn)的一個(gè)方法物蝙,終于我們又可以看回StringRequest了炎滞,我們看看StringRequest方法做了些什么敢艰。

  @Override
  protected Response<String> parseNetworkResponse(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));
  }

可以看到它將response轉(zhuǎn)換成了String類型的Response,所以其實(shí)這個(gè)方法就是將NetworkResponse轉(zhuǎn)換成了對應(yīng)數(shù)據(jù)類型册赛。拿到了Respnse<String>之后我們就可以通過mDelivery.postResponse(request, response);或mDelivery.postError(request, volleyError);將這個(gè)響應(yīng)钠导,通過StringRequst的deliverResponse方法,然后通過mListener將結(jié)果回調(diào)出去森瘪。 至此一次StringRequst請求我們就分析完了一半了牡属。一起來看看mDelivery做了什么。

這里mDelivery是一個(gè)ExecutorDelivery對象扼睬,為什么逮栅?見下:

  public RequestQueue(Cache cache, Network network, int threadPoolSize) {
       this(cache, network, threadPoolSize,
               new ExecutorDelivery(new Handler(Looper.getMainLooper())));
   }

并且handler的消息處理都是在主線程哦。因?yàn)?code>new Handler(Looper.getMainLooper()).

那就讓我們一起來看看 ExecutorDelivery這個(gè)類吧窗宇。
首先PO一個(gè)構(gòu)造函數(shù)措伐。

    /** 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);
          }
      };
  }

用了一個(gè)Executor異步執(zhí)行框架用來封裝handler。然后看看postResponsepostError方法军俊。

   @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));
   }

可以看到最終都將Response和Reqeust傳遞到了一個(gè)ResponseDeliveryRunable里面然后執(zhí)行侥加。快馬加鞭粪躬,我們看看ResponseDeliveryRunable的run方法担败。

  public void run() {
           // 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();
           }
      }

注意在它處理請求之前它又一次判斷了請求是否被取消了昔穴,果然官方的代碼有一些地方就是比較謹(jǐn)慎。記得在NetworkDispatcher的run方法的時(shí)候就已經(jīng)判斷過一次了提前,這樣多次判斷可以避免在請求返回之前用戶取消了吗货。我們應(yīng)該也學(xué)著這么謹(jǐn)慎,從而避免一些不必要的處理狈网。如果Request沒有被取消掉那么就通過mResponse.isSuccess()判斷一下請求是否成功卿操,那么它如何判斷請求是否成功的呢。

    /**
    * Returns whether this response is considered successful.
    */
   public boolean isSuccess() {
       return error == null;
   }

原來他只是簡單的根據(jù)error是否為空來判斷請求成功了沒有孙援,你可以跟蹤一下代碼害淤,就知道當(dāng)請求成功時(shí)候error是null的,失敗的時(shí)候error是一個(gè)VolleyError對象buweinull拓售。如果成功了就調(diào)用mRequest.deliverResponse(mResponse.result);,然后就成功的請求到數(shù)據(jù)啦窥摄。RequestdeliverResponse方法如下。

@Override
 protected void deliverResponse(String response) {
     mListener.onResponse(response);
 }   
 

至此我們就將請求結(jié)果回調(diào)到一開始new Request的listener里面啦础淤。我們對StringRequest的請求過程的分析崭放,也分析到一半了。為什么是一半呢鸽凶。因?yàn)?code>CacheDispatcher和RequestQueue.add還沒有分析呢币砂,這也是很重要的知識(shí)點(diǎn)。 由于篇幅原因玻侥,我分兩篇文章來寫决摧。下一篇,馬上就會(huì)發(fā)布的凑兰,敬請期待U谱!
一口氣寫了這么多姑食,累了波岛。閃了。 有問題歡迎留言音半。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末则拷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子曹鸠,更是在濱河造成了極大的恐慌煌茬,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件物延,死亡現(xiàn)場離奇詭異宣旱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)叛薯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門浑吟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笙纤,“玉大人,你說我怎么就攤上這事组力∈∪荩” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵燎字,是天一觀的道長腥椒。 經(jīng)常有香客問我,道長候衍,這世上最難降的妖魔是什么笼蛛? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蛉鹿,結(jié)果婚禮上滨砍,老公的妹妹穿的比我還像新娘。我一直安慰自己妖异,他們只是感情好惋戏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著他膳,像睡著了一般响逢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棕孙,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天舔亭,我揣著相機(jī)與錄音,去河邊找鬼散罕。 笑死分歇,一個(gè)胖子當(dāng)著我的面吹牛傀蓉,可吹牛的內(nèi)容都是我干的欧漱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼葬燎,長吁一口氣:“原來是場噩夢啊……” “哼误甚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谱净,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤窑邦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后壕探,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冈钦,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡李请,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年瞧筛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了厉熟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡较幌,死狀恐怖揍瑟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乍炉,我是刑警寧澤绢片,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站岛琼,受9級特大地震影響底循,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜槐瑞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一此叠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧随珠,春花似錦灭袁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至显沈,卻和暖如春软瞎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拉讯。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工涤浇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人魔慷。 一個(gè)月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓只锭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親院尔。 傳聞我的和親對象是個(gè)殘疾皇子蜻展,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容