(干貨) Android Volley框架源碼詳細(xì)解析

前言

經(jīng)常接觸Android網(wǎng)絡(luò)編程的我們,對(duì)于Volley肯定不陌生性雄,但我們不禁要問(wèn)办悟,對(duì)于Volley我們真的很了解嗎?Volley的內(nèi)部是怎樣實(shí)現(xiàn)的铺然?為什么幾行代碼就能快速搭建好一個(gè)網(wǎng)絡(luò)請(qǐng)求赋铝?我們不但要知其然革骨,也要知其所以然,抱著這樣的目的筑凫,本文主要詳細(xì)講述Volley的源碼巍实,對(duì)內(nèi)部流程進(jìn)行詳細(xì)解析。

Part 1.從RequestQueue說(shuō)起

(1)還記得搭建請(qǐng)求的第一步是什么嗎瓦盛?是新建一個(gè)請(qǐng)求隊(duì)列,比如說(shuō)這樣:

RequestQueue queue = Volley.newRequestQueue(context)

雖然表面上只是一句代碼的事情嘱吗,但是背后做了很多準(zhǔn)備工作,我們追蹤源碼绕德,找到Volley#newRequestQueue()方法:

 /**
   * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
   * You may set a maximum size of the disk cache in bytes.
   *
   * @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.
   * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size.
   * @return A started {@link RequestQueue} instance.
   */
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
    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) {
    }

     /**
       * 根據(jù)不同的系統(tǒng)版本號(hào)實(shí)例化不同的請(qǐng)求類踪蹬,如果版本號(hào)小于9,用HttpClient
       * 如果版本號(hào)大于9夺蛇,用HttpUrlConnection
       */
    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));
        }
    }
      //把實(shí)例化的stack傳遞進(jìn)BasicNetwork娶聘,實(shí)例化Network
    Network network = new BasicNetwork(stack);
    RequestQueue queue;
    if (maxDiskCacheBytes <= -1){
       // No maximum size specified
       //實(shí)例化RequestQueue類 
       queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    }
    else {
       // Disk cache size specified
       queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
    }
     //調(diào)用RequestQueue的start()方法
    queue.start();
    return queue;
}

首先我們看參數(shù)宦焦,有三個(gè),實(shí)際上我們默認(rèn)使用了只有一個(gè)參數(shù)context的方法,這個(gè)是對(duì)應(yīng)的重載方法歹篓,最終調(diào)用的是三個(gè)參數(shù)的方法庄撮,context是上下文環(huán)境;stack代表需要使用的網(wǎng)絡(luò)連接請(qǐng)求類毙籽,這個(gè)一般不用設(shè)置洞斯,方法內(nèi)部會(huì)根據(jù)當(dāng)前系統(tǒng)的版本號(hào)調(diào)用不同的網(wǎng)絡(luò)連接請(qǐng)求類(HttpUrlConnection和HttpClient);最后一個(gè)參數(shù)是緩存的大小坑赡。接著我們看方法內(nèi)部烙如,這里先創(chuàng)建了緩存文件,然后根據(jù)不同的系統(tǒng)版本號(hào)實(shí)例化不同的請(qǐng)求類毅否,用stack引用這個(gè)類。接著又實(shí)例化了一個(gè)BasicNetwork,這個(gè)類在下面會(huì)說(shuō)到。然后到了實(shí)際實(shí)例化請(qǐng)求隊(duì)列的地方:new RequestQueue()翻默,這里接收兩個(gè)參數(shù)翘单,分別是緩存和network(BasicNetwork)。實(shí)例化RequestQueue后,調(diào)用了start()方法堪滨,最后返回這個(gè)RequestQueue。
 ≈坪洹(2)我們跟著RequestQueue看看它的構(gòu)造器做了哪些工作:

/**
     * 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;
        //實(shí)例化網(wǎng)絡(luò)請(qǐng)求數(shù)組,數(shù)組大小默認(rèn)是4
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }

    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                //ResponseDelivery是一個(gè)接口骇钦,實(shí)現(xiàn)類是ExecutorDelivery
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

    public RequestQueue(Cache cache, Network network) {
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }

可以看到鳍烁,把傳遞過(guò)來(lái)的cache和network作為變量傳遞給了四個(gè)參數(shù)的構(gòu)造器,在這里,初始化了RequestQueue的幾個(gè)成員變量:mCache(文件緩存)君编、mNetwork(BasicNetwork實(shí)例)、mDispatchers(網(wǎng)絡(luò)請(qǐng)求線程數(shù)組)力崇、以及mDelivery(派發(fā)請(qǐng)求結(jié)果的接口)贞岭,具體意義可看上面的注解。
 ⌒谥摇(3)構(gòu)造完RequestQueue后犬庇,從(1)可知,最后調(diào)用了它的start()方法,我們來(lái)看看這個(gè)方法,RequestQueue#start()

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

首先實(shí)例化了CacheDispatcher,CacheDispatcher類繼承自Thread,接著調(diào)用了它的start()方法胞锰,開(kāi)始了一條新的緩存線程澎办。接著是一個(gè)for循環(huán)澎羞,根據(jù)設(shè)置的mDispatchers數(shù)組大小來(lái)開(kāi)啟多個(gè)網(wǎng)絡(luò)請(qǐng)求線程僵闯,默認(rèn)是4條網(wǎng)絡(luò)請(qǐng)求線程航攒。
  到目前為止庆杜,Volley.newRequestQueue()方法完成了,即我們的網(wǎng)絡(luò)請(qǐng)求第一步公你,建立請(qǐng)求隊(duì)列完成益兄。
  先小結(jié)一下:建立請(qǐng)求隊(duì)列所做的工作是古今,創(chuàng)建文件緩存(默認(rèn))抵碟,實(shí)例化BasicNetwork苦囱,實(shí)例化Delivery用于發(fā)送線程請(qǐng)求撼唾,創(chuàng)建一條緩存線程和四條網(wǎng)絡(luò)請(qǐng)求線程(默認(rèn))并運(yùn)行诺苹。

Part 2.網(wǎng)絡(luò)請(qǐng)求的實(shí)現(xiàn)原理

在創(chuàng)建完請(qǐng)求隊(duì)列后,接著就是建立一個(gè)請(qǐng)求雹拄,請(qǐng)求的方式可以是StringRequest收奔、JsonArrayRequest或者ImageRequest等,那么這些請(qǐng)求的背后原理是什么呢滓玖?我們拿最簡(jiǎn)單的StringRequest來(lái)說(shuō)坪哄,它繼承自Request,而Request則是所有請(qǐng)求的父類势篡,所以說(shuō)如果你要自定義一個(gè)網(wǎng)絡(luò)請(qǐng)求翩肌,就應(yīng)該繼承自Request。接下來(lái)我們看看StringRequest的源碼禁悠,因?yàn)椴还躌equest的子類是什么念祭,大體的實(shí)現(xiàn)思路都是一致的,所以我們弄懂了StringRequest绷蹲,那么對(duì)于其他的請(qǐng)求類的理解是相通的棒卷。如下是StringRequest源碼:

public class StringRequest extends Request<String> {
    private Listener<String> mListener;

    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
    ...
    @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

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

源碼并不長(zhǎng)顾孽,我們主要關(guān)注的是deliverResponse方法和parseNetworkResponse祝钢”裙妫可以看出,這兩個(gè)方法都是重寫(xiě)的拦英,我們翻看父類Request的對(duì)應(yīng)方法蜒什,發(fā)現(xiàn)是抽象方法,說(shuō)明這兩個(gè)方法在每一個(gè)自定義的Request中都必須重寫(xiě)疤估。這里簡(jiǎn)單說(shuō)說(shuō)這兩個(gè)方法的作用。先看deliverResponse方法:它內(nèi)部調(diào)用了mListener.onResponse(response)方法,而這個(gè)方法正是我們?cè)趯?xiě)一個(gè)請(qǐng)求的時(shí)候妒貌,添加的listener所重寫(xiě)的onResponse方法俩垃,也就是說(shuō),響應(yīng)成功后在這里調(diào)用了onResponse()方法慷荔。接著看pareNetworkResponse方法雕什,可以看出這里主要是對(duì)response響應(yīng)做出一些處理∠跃В可以對(duì)比一下不同請(qǐng)求類的這個(gè)方法贷岸,都會(huì)不同的,所以說(shuō)磷雇,這個(gè)方法是針對(duì)不同的請(qǐng)求類型而對(duì)響應(yīng)做出不同的處理偿警。比如說(shuō),如果是StringRequest唯笙,則將響應(yīng)包裝成String類型螟蒸;如果是JsonObjectRequest,則將響應(yīng)包裝成JsonObject崩掘。那么現(xiàn)在應(yīng)該清楚了:對(duì)于想要得到某一種特殊類型的請(qǐng)求尿庐,我們可以自定義一個(gè)Request,重寫(xiě)這兩個(gè)方法即可呢堰。
  這里小結(jié)一下:Request類做的工作主要是初始化一些參數(shù)抄瑟,比如說(shuō)請(qǐng)求類型、請(qǐng)求的url枉疼、錯(cuò)誤的回調(diào)方法皮假;而它的任一子類重寫(xiě)deliverResponse方法來(lái)實(shí)現(xiàn)成功的回調(diào),重寫(xiě)parseNetworkResponse()方法來(lái)處理響應(yīng)數(shù)據(jù)骂维;至此惹资,一個(gè)完整的Request請(qǐng)求搭建完畢。

Part 3.添加請(qǐng)求

前面已經(jīng)完成了請(qǐng)求隊(duì)列的創(chuàng)建航闺,Request請(qǐng)求的創(chuàng)建褪测,那么接下來(lái)就是把請(qǐng)求添加進(jìn)隊(duì)列了猴誊。我們看RequestQueue#add()源碼

/**
 * Adds a Request to the dispatch queue.
 * @param request The request to service
 * @return The passed-in request
 */
public <T> Request<T> add(Request<T> request) {
    // Tag the request as belonging to this queue and add it to the set of current requests.
    //標(biāo)記當(dāng)前請(qǐng)求,表示這個(gè)請(qǐng)求由當(dāng)前RequestQueue處理
    request.setRequestQueue(this);
    synchronized (mCurrentRequests) {
        mCurrentRequests.add(request);
    }
    // Process requests in the order they are added.
    //獲得當(dāng)前請(qǐng)求的序號(hào)
    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");
    // If the request is uncacheable, skip the cache queue and go straight to the network.
    //如果請(qǐng)求不能緩存侮措,直接添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列懈叹,默認(rèn)是可以緩存
    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.
    // 鎖定當(dāng)前代碼塊,只能一條線程執(zhí)行
    synchronized (mWaitingRequests) {
        String cacheKey = request.getCacheKey();

        //是否有相同請(qǐng)求正在處理
        if (mWaitingRequests.containsKey(cacheKey)) {
            // There is already a request in flight. Queue up.
            //如果有相同請(qǐng)求正在處理分扎,那么把這個(gè)請(qǐng)求放進(jìn)mWaitingRequest中澄成,等待。
            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.
            //沒(méi)有相同的請(qǐng)求畏吓,那么把請(qǐng)求放進(jìn)mWaitingRequests中墨状,同時(shí)也放進(jìn)mCacheQueue緩存隊(duì)列中
            //這代表這個(gè)請(qǐng)求已經(jīng)開(kāi)始在緩存線程中運(yùn)行了
            mWaitingRequests.put(cacheKey, null);
            mCacheQueue.add(request);
        }
        return request;
    }
}

結(jié)合相應(yīng)的注釋,我們得出如下結(jié)論:在這個(gè)add方法中菲饼,主要判斷一個(gè)Request請(qǐng)求是否可以緩存(默認(rèn)是可以緩存的)肾砂,如果不可以則直接添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列開(kāi)始網(wǎng)絡(luò)通信;如果可以宏悦,則進(jìn)一步判斷當(dāng)前是否有相同的請(qǐng)求正在進(jìn)行镐确,如果有相同的請(qǐng)求,則讓這個(gè)請(qǐng)求排隊(duì)等待肛根,如果沒(méi)有相同的請(qǐng)求辫塌,則直接放進(jìn)緩存隊(duì)列中。如果對(duì)此還有什么疑問(wèn)派哲,可以看下面的流程圖(圖片來(lái)自網(wǎng)絡(luò)):

RequestQueue#add()方法流程圖

Part 4.緩存線程

在part1的最后實(shí)例化了緩存線程并開(kāi)始運(yùn)行臼氨,一直處于等待狀態(tài),而上面把請(qǐng)求添加進(jìn)了緩存線程芭届,此時(shí)緩存線程就開(kāi)始真正的工作了储矩。我們來(lái)看緩存線程的源碼,主要看它的run()方法褂乍,CacheDispatcher#run():

@Override
public void run() {
    if (DEBUG) VolleyLog.v("start new dispatcher");
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

    // Make a blocking call to initialize the cache.
    mCache.initialize();

    Request<?> request;
    while (true) {
        // release previous request object to avoid leaking request object when mQueue is drained.
        request = null;
        try {
            // Take a request from the queue.
            //從緩存隊(duì)列中取出請(qǐng)求
            request = mCacheQueue.take();
        } ...
        try {
            request.addMarker("cache-queue-take");

            // If the request has been canceled, don't bother dispatching it.
            if (request.isCanceled()) {
                request.finish("cache-discard-canceled");
                continue;
            }

            // Attempt to retrieve this item from cache.
            //從文件緩存中取出這個(gè)請(qǐng)求的結(jié)果
            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.
            //判斷緩存是否過(guò)期
            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");
            //先將響應(yīng)的結(jié)果包裝成NetworkResponse持隧,然后調(diào)用Request子類的
            //parseNetworkResponse方法解析數(shù)據(jù)
            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.
                //調(diào)用ExecutorDelivey#postResponse方法
                mDelivery.postResponse(request, response);
            } else {
                ....
            }
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
        }
    }
}

在run()方法中,我們可以看到最開(kāi)始有一個(gè)while(true)循環(huán)逃片,表示它一直在等待緩存隊(duì)列的新請(qǐng)求的出現(xiàn)屡拨。接著,先判斷這個(gè)請(qǐng)求是否有對(duì)應(yīng)的緩存結(jié)果褥实,如果沒(méi)有則直接添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列呀狼;接著再判斷這個(gè)緩存結(jié)果是否過(guò)期了,如果過(guò)期則同樣地添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列损离;接下來(lái)便是對(duì)緩存結(jié)果的處理了哥艇,我們可以看到,先是把緩存結(jié)果包裝成NetworkResponse類僻澎,然后調(diào)用了Request的parseNetworkResponse貌踏,這個(gè)方法我們?cè)趐art2說(shuō)過(guò)十饥,子類需要重寫(xiě)這個(gè)方法來(lái)處理響應(yīng)數(shù)據(jù)。最后祖乳,把處理好的數(shù)據(jù)post到主線程逗堵,這里用到了ExecutorDelivery#postResponse()方法,下面會(huì)分析到凡资。
  小結(jié):CacheDispatcher線程主要對(duì)請(qǐng)求進(jìn)行判斷砸捏,是否已經(jīng)有緩存谬运,是否已經(jīng)過(guò)期隙赁,根據(jù)需要放進(jìn)網(wǎng)絡(luò)請(qǐng)求隊(duì)列。同時(shí)對(duì)相應(yīng)結(jié)果進(jìn)行包裝梆暖、處理伞访,然后交由ExecutorDelivery處理。這里以一張流程圖顯示它的完整工作流程:

CacheDispatcher線程工作流程

Part 5.網(wǎng)絡(luò)請(qǐng)求線程

上面提到轰驳,請(qǐng)求不能緩存厚掷、緩存結(jié)果不存在、緩存過(guò)期的時(shí)候會(huì)把請(qǐng)求添加進(jìn)請(qǐng)求隊(duì)列级解,此時(shí)一直等待的網(wǎng)絡(luò)請(qǐng)求線程由于獲取到請(qǐng)求冒黑,終于要開(kāi)始工作了,我們來(lái)看NetworkDispatcher#run()方法

@Override
    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 {
                // Take a request from the queue.
                request = mQueue.take();
            } ...

            try {
                ...
                // Perform the network request.
                //調(diào)用BasicNetwork實(shí)現(xiàn)類進(jìn)行網(wǎng)絡(luò)請(qǐng)求勤哗,并獲得響應(yīng) 1
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                ...

                // Parse the response here on the worker thread.
                //對(duì)響應(yīng)進(jìn)行解析
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");
                ...
                request.markDelivered();
                mDelivery.postResponse(request, response);

                // Write to cache if applicable.
                //將響應(yīng)結(jié)果寫(xiě)進(jìn)緩存
                if (request.shouldCache() && response.cacheEntry != null) {    
                   mCache.put(request.getCacheKey(), response.cacheEntry);   
                   request.addMarker("network-cache-written");
                }
            } ...
        }
    }

源碼做了適當(dāng)?shù)膭h減抡爹,大體上和CacheDispatcher的邏輯相同,這里關(guān)注①號(hào)代碼芒划,這里調(diào)用了BasicNetwork#perfromRequest()方法冬竟,把請(qǐng)求傳遞進(jìn)去,可以猜測(cè)民逼,這個(gè)方法內(nèi)部實(shí)現(xiàn)了網(wǎng)絡(luò)請(qǐng)求的相關(guān)操作泵殴,那么我們進(jìn)去看看,BasicNetwork#perfromRequest()

@Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());

                httpResponse = mHttpStack.performRequest(request, headers);  // 1
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,  
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);  // 2
                    }

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

我們主要看①號(hào)代碼拼苍,mHttpStack.performRequest();這里調(diào)用了mHttpStack的performRequest方法笑诅,那么mHttpStack是什么呢?我們可以翻上去看看part1處實(shí)例化BasicNetwork的時(shí)候傳遞的stack值疮鲫,該值就是根據(jù)不同的系統(tǒng)版本號(hào)而實(shí)例化的HttpStack對(duì)象(版本號(hào)大于9的是HurlStack吆你,小于9的是HttpClientStack),由此可知棚点,這里實(shí)際調(diào)用的是HurlStack.performRequest()方法早处,方法的內(nèi)部基本是關(guān)于HttpUrlConnection的邏輯代碼,這里就不展開(kāi)說(shuō)了瘫析∑霭穑可以這么說(shuō):HurlStack封裝好了HttpUrlConnection默责,而HttpClientStack封裝了HttpClient。該方法返回了httpResponse咸包,接著把這個(gè)響應(yīng)交由②處處理桃序,封裝成NetworkResponse對(duì)象并返回。在NetworkDispatcher#run()方法獲取返回的NetworkResponse對(duì)象后烂瘫,對(duì)響應(yīng)解析媒熊,通過(guò)ExecutorDelivery#postResponse()方法回調(diào)解析出來(lái)的數(shù)據(jù),這個(gè)過(guò)程和CacheDispatcher相同坟比。

Part 6.ExecutorDelivery 通知主線程

在CacheDispatcher和NetworkDispatcher中最后都有調(diào)用到ExecutorDelivery#postResponse()方法芦鳍,那么這個(gè)方法到底是做什么呢?由于緩存線程和網(wǎng)絡(luò)請(qǐng)求線程都不是主線程葛账,所以主線程需要有“人”通知它網(wǎng)絡(luò)請(qǐng)求已經(jīng)完成了柠衅,而這個(gè)“人”正是由ExecutorDelivery充當(dāng)。在完成請(qǐng)求后籍琳,通過(guò)ExecutorDelivery#postResponse()方法菲宴,最終會(huì)回調(diào)到主線程中重寫(xiě)的onResponse()方法。我們看看這個(gè)方法的源碼ExecutorDelivery#postResponse()

    @Override
    public void postResponse(Request<?> request, Response<?> response) {
        postResponse(request, response, null);
    }

    @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

在方法內(nèi)部調(diào)用了mResponsePoster#execute()方法趋急,那么喝峦,這個(gè)mResponsePoster是在哪里來(lái)的呢?其實(shí)這個(gè)成員變量是在ExecutorDelivery實(shí)例化的時(shí)候同時(shí)實(shí)例化的呜达,而ExecutorDelivery則是在RequestQueue實(shí)例化的時(shí)候同時(shí)實(shí)例化的谣蠢,讀者可以自行查看相應(yīng)的構(gòu)造方法,其實(shí)這些工作在par 1建立請(qǐng)求隊(duì)列的時(shí)候已經(jīng)全部做好了闻丑。接著我們可以看以下代碼漩怎,ExecutorDelivery#ExecutorDelivery():

public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        //實(shí)例化Executor,并且重寫(xiě)execute方法
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                //這里獲取的handler是主線程的handler嗦嗡,可看part 1 (2)
                handler.post(command);
            }
        };
    }

execute()方法接收一個(gè)Runnable對(duì)象勋锤,那么我們回到上面的

mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));

可以看出這里實(shí)例化了一個(gè)ResponseDeliveryRunnable對(duì)象作為Runnable對(duì)象。而這里的ResponseDeliveryRunnable則是當(dāng)前類Executor的一個(gè)內(nèi)部類侥祭,實(shí)現(xiàn)了Runnable接口叁执,我們來(lái)看看:

/**
     * A Runnable used for delivering network responses to a listener on the
     * main thread.
     */
    @SuppressWarnings("rawtypes")
    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        //構(gòu)造方法
        ...
        @Override
        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);  // 1
            } else {
                mRequest.deliverError(mResponse.error);
            }
            ...
            }
       }
    }

在上面,①號(hào)代碼回調(diào)到了Request的deliverResponse方法矮冬,即此時(shí)通知了主線程谈宛,請(qǐng)求已經(jīng)完成,此時(shí)在Request子類重寫(xiě)的deliverResponse方法則會(huì)調(diào)用onResponse()方法胎署,那么我們的Activity就能方便調(diào)用解析好的請(qǐng)求結(jié)果了吆录。
  到目前為止,關(guān)于Volley的源碼解析完畢琼牧。

總結(jié)

用google官方的一幅圖來(lái)說(shuō)明以上所說(shuō)各個(gè)部分的關(guān)系(圖片來(lái)自網(wǎng)絡(luò)):


Volley流程圖

  藍(lán)色部分代表主線程恢筝,綠色部分代表緩存線程哀卫,橙色部分代表網(wǎng)絡(luò)請(qǐng)求線程。讀者可以根據(jù)這幅圖所展示的關(guān)系撬槽,再仔細(xì)體會(huì)part 1-6所說(shuō)的流程此改,那么就很容易理解了。最后感謝你的閱讀侄柔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末共啃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子暂题,更是在濱河造成了極大的恐慌移剪,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敢靡,死亡現(xiàn)場(chǎng)離奇詭異挂滓,居然都是意外死亡苦银,警方通過(guò)查閱死者的電腦和手機(jī)啸胧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)幔虏,“玉大人纺念,你說(shuō)我怎么就攤上這事∠肜ǎ” “怎么了陷谱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瑟蜈。 經(jīng)常有香客問(wèn)我烟逊,道長(zhǎng),這世上最難降的妖魔是什么铺根? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任宪躯,我火速辦了婚禮,結(jié)果婚禮上位迂,老公的妹妹穿的比我還像新娘访雪。我一直安慰自己,他們只是感情好掂林,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布臣缀。 她就那樣靜靜地躺著,像睡著了一般泻帮。 火紅的嫁衣襯著肌膚如雪精置。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天锣杂,我揣著相機(jī)與錄音脂倦,去河邊找鬼饲常。 笑死,一個(gè)胖子當(dāng)著我的面吹牛狼讨,可吹牛的內(nèi)容都是我干的贝淤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼政供,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼播聪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起布隔,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤离陶,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后衅檀,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體招刨,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年哀军,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沉眶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杉适,死狀恐怖谎倔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猿推,我是刑警寧澤片习,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蹬叭,受9級(jí)特大地震影響藕咏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秽五,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一孽查、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧筝蚕,春花似錦卦碾、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至坯沪,卻和暖如春绿映,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工叉弦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丐一,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓淹冰,卻偏偏與公主長(zhǎng)得像库车,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子樱拴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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