Volley源碼閱讀

Volley源碼閱讀

如何閱讀源碼?關(guān)于這個(gè)翔忽,前人有很多的方法玷禽。一般我分析這類(lèi)工程赫段,首先做的第一步是打開(kāi)這類(lèi)工程的開(kāi)發(fā)者官網(wǎng),官網(wǎng)上都會(huì)對(duì)這個(gè)工具庫(kù)進(jìn)行介紹矢赁。比如糯笙,它是什么?能解決什么問(wèn)題撩银?與其它同類(lèi)工具比較给涕,有什么優(yōu)點(diǎn)和劣勢(shì)(可能有些工程不會(huì)在官網(wǎng)上提到這個(gè))。
想要分析它额获,首先要知道開(kāi)發(fā)者是怎么開(kāi)發(fā)它的够庙,開(kāi)發(fā)過(guò)程中使用了什么技巧,軟件的架構(gòu)是什么樣的抄邀。還有就是教程耘眨,開(kāi)發(fā)者一般都給出了使用教程。從使用教程中境肾,我們知道程序的使用方式剔难,也就是程序的入口。還有就是有一些前輩已經(jīng)分析過(guò)了源碼奥喻,他們的博客能為我們解決不少困惑偶宫。
接下來(lái),我們開(kāi)始源碼分析环鲤。從教程中纯趋,我們知道,要想發(fā)起一個(gè)請(qǐng)求冷离,我們至少需要?jiǎng)?chuàng)建兩個(gè)對(duì)象:RequestQueue和Request吵冒。Request就是我們想要發(fā)起的請(qǐng)求,可以設(shè)置請(qǐng)求方式西剥、Header和Url痹栖。RequestQueue是一個(gè)執(zhí)行Request的對(duì)象。
創(chuàng)建一個(gè)RequestQueue有幾個(gè)簡(jiǎn)單的方式蔫耽,分別是:

    /**
     * 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 A {@link BaseHttpStack} to use for the network, or null for default.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack)

    /**
     * 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.
     * @deprecated Use {@link #newRequestQueue(Context, BaseHttpStack)} instead to avoid depending
     *     on Apache HTTP. This method may be removed in a future release of Volley.
     */
    @Deprecated
    @SuppressWarnings("deprecation")
    public static RequestQueue newRequestQueue(Context context, HttpStack stack)

    private static RequestQueue newRequestQueue(Context context, Network network)

    /**
     * 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.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context)

四個(gè)構(gòu)建方式,第一個(gè)構(gòu)建方式留夜,入?yún)⒂蠧ontext和BaseHttpStack,從注釋中匙铡,我們知道,Context是用于創(chuàng)建緩存地址碍粥,而B(niǎo)aseHTTPStack用于網(wǎng)路請(qǐng)求鳖眼,如果傳的是空的,則會(huì)使用默認(rèn)的嚼摩。然后钦讳,會(huì)返回一個(gè)已經(jīng)開(kāi)始運(yùn)行的RequestQueue實(shí)例(即矿瘦,創(chuàng)建了緩存線(xiàn)程和工作線(xiàn)程池,并啟動(dòng)了他們)愿卒。
第二個(gè)被廢棄了缚去,我們不仔細(xì)講了,可以看注釋琼开。
第三個(gè)比較簡(jiǎn)單易结,傳入了一個(gè)Context和Network,同樣Network也是用來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的。
第四個(gè)是我們常用的柜候,直接傳入Context搞动,用于創(chuàng)建緩存路徑。其實(shí)渣刷,從源碼中看鹦肿,它其實(shí)調(diào)用了第一個(gè)構(gòu)建方式,只不過(guò)第二個(gè)參數(shù)傳的是空辅柴。
那么我們來(lái)看看第一個(gè)和第三個(gè)構(gòu)建方式的源碼箩溃。

    /** Default on-disk cache directory. */
    private static final String DEFAULT_CACHE_DIR = "volley";

    /**
     * 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 A {@link BaseHttpStack} to use for the network, or null for default.
     * @return A started {@link RequestQueue} instance.
     */
    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 {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                // At some point in the future we'll move our minSdkVersion past Froyo and can
                // delete this fallback (along with all Apache HTTP code).
                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;
    }

先看第三個(gè)構(gòu)建方式,因?yàn)槁凳叮谝粋€(gè)碾篡、第二個(gè)和第四個(gè)都間接的調(diào)用了第三個(gè)構(gòu)建方式。(其實(shí)正常的分析過(guò)程中筏餐,我是先看的第四個(gè)構(gòu)建方式开泽,然后再分析到了第一個(gè),最后是第三個(gè)魁瞪。嗯穆律,對(duì)的,這種方法叫做順藤摸瓜)
這個(gè)構(gòu)建方式是靜態(tài)私有的导俘,有兩個(gè)參數(shù)峦耘,分別是Context和Network。Context用于創(chuàng)建緩存路徑旅薄;Network用于實(shí)現(xiàn)網(wǎng)絡(luò)傳輸辅髓。函數(shù)的第一行,根據(jù)context獲取App的cacheDir和Volley默認(rèn)的相對(duì)DIR少梁,創(chuàng)建了一個(gè)文件cacheDir洛口。然后,根據(jù)這個(gè)cacheDir創(chuàng)建了一個(gè)DiskBasedCache凯沪,使用這個(gè)DiskBasedCache和傳進(jìn)來(lái)的network創(chuàng)建了一個(gè)RequestQueue第焰。調(diào)用RequestQueue的start()方法,啟動(dòng)它妨马。然后返回這個(gè)RequestQueue挺举。這個(gè)印證了杀赢,在教程中說(shuō)的,使用newRequestQueue方法湘纵,會(huì)創(chuàng)建一個(gè)RequestQueue并啟動(dòng)它脂崔。關(guān)鍵點(diǎn)是RequestQueue類(lèi),我們打開(kāi)源碼分析它瞻佛。

RequestQueue類(lèi)

這個(gè)類(lèi)的描述是:

/**
 * A request dispatch queue with a thread pool of dispatchers.
 *
 * <p>Calling {@link #add(Request)} will enqueue the given Request for dispatch, resolving from
 * either cache or network on a worker thread, and then delivering a parsed response on the main
 * thread.
 */

簡(jiǎn)單說(shuō)脱篙,就是一個(gè)帶有派發(fā)者線(xiàn)程池的請(qǐng)求派發(fā)隊(duì)列。調(diào)用add()方法伤柄,將會(huì)入隊(duì)你傳入的請(qǐng)求绊困,分析是從緩存還是在工作線(xiàn)程的網(wǎng)絡(luò)請(qǐng)求中得到結(jié)果,然后在主線(xiàn)程中將的解析后的響應(yīng)結(jié)果傳送出來(lái)适刀。
分析一個(gè)具體的類(lèi)秤朗,看完類(lèi)的基本描述后,我首先會(huì)看看它的構(gòu)造函數(shù)笔喉。這個(gè)類(lèi)有三個(gè)構(gòu)造函數(shù),分別是:

    /** Number of network request dispatcher threads to start. */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    /** Cache interface for retrieving and storing responses. */
    private final Cache mCache;

    /** Network interface for performing requests. */
    private final Network mNetwork;

    /** Response delivery mechanism. */
    private final ResponseDelivery mDelivery;

    /** The network dispatchers. */
    private final NetworkDispatcher[] mDispatchers;

    /** The cache dispatcher. */
    private CacheDispatcher mCacheDispatcher;

    private final List<RequestFinishedListener> mFinishedListeners = new ArrayList<>();

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

    /**
     * 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
     */
    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(
                cache,
                network,
                threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

    /**
     * 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
     */
    public RequestQueue(Cache cache, Network network) {
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }

我們直接分析第一個(gè)構(gòu)造函數(shù)取视,因?yàn)楹竺鎯蓚€(gè)構(gòu)造函數(shù)均是調(diào)用第一個(gè)構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)功能的。
注釋說(shuō)了常挚,這個(gè)方法的作用是創(chuàng)建線(xiàn)程池作谭,當(dāng)然,此時(shí)還沒(méi)有啟動(dòng)奄毡,只有調(diào)用了start()方法折欠,才會(huì)開(kāi)啟。有四個(gè)參數(shù)吼过,分別是:cache锐秦、network、threadPoolSize和ResponseDelivery盗忱。cache用于持久化響應(yīng)結(jié)果到磁盤(pán)上酱床;network用于完成HTTP請(qǐng)求;threadPoolSize用于設(shè)置網(wǎng)絡(luò)派發(fā)者線(xiàn)程的數(shù)量趟佃;ResponseDelivery接口扇谣,用于發(fā)布響應(yīng)和錯(cuò)誤。在調(diào)用該方法時(shí)闲昭,其將你傳入的這些參數(shù)罐寨,賦值到私有變量中。并根據(jù)你傳的threadPoolSize汤纸,創(chuàng)建線(xiàn)程池衩茸。
第二個(gè)構(gòu)造函數(shù)有三個(gè)參數(shù)芹血,調(diào)用了第一個(gè)構(gòu)造函數(shù)贮泞,并創(chuàng)建一個(gè)ResponseDelivery楞慈。這個(gè)ResponseDelivery是回調(diào)的關(guān)鍵。我們看一下它的源碼啃擦。

ResponseDelivery

它是一個(gè)接口囊蓝,定義了三個(gè)方法:

public interface ResponseDelivery {
    /** Parses a response from the network or cache and delivers it. */
    void postResponse(Request<?> request, Response<?> response);

    /**
     * Parses a response from the network or cache and delivers it. The provided Runnable will be
     * executed after delivery.
     */
    void postResponse(Request<?> request, Response<?> response, Runnable runnable);

    /** Posts an error for the given request. */
    void postError(Request<?> request, VolleyError error);
}
  • void postResponse(Request<?> request, Response<?> response);
    解析來(lái)自網(wǎng)絡(luò)或緩存的響應(yīng),并傳送到它
  • void postResponse(Request<?> request, Response<?> response, Runnable runnable);
    同上令蛉,不同的是聚霜,多一個(gè)參數(shù)Runnable,這個(gè)Runnable將會(huì)在傳送后被執(zhí)行
  • void postError(Request<?> request, VolleyError error);
    為指定的請(qǐng)求發(fā)送一個(gè)錯(cuò)誤
    實(shí)際上珠叔,我們使用的ExecutorDelivery蝎宇,它是ResponseDelivery的實(shí)現(xiàn)類(lèi)。
import android.os.Handler;
import java.util.concurrent.Executor;

/** Delivers responses and errors. */
public class ExecutorDelivery implements ResponseDelivery {
    /** 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);
                    }
                };
    }

    /**
     * Creates a new response delivery interface, mockable version for testing.
     *
     * @param executor For running delivery tasks
     */
    public ExecutorDelivery(Executor executor) {
        mResponsePoster = executor;
    }

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

    @Override
    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. */
    @SuppressWarnings("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;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // NOTE: If cancel() is called off the thread that we're currently running in (by
            // default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
            // won't be called, since it may be canceled after we check isCanceled() but before we
            // deliver the response. Apps concerned about this guarantee must either call cancel()
            // from the same thread or implement their own guarantee about not invoking their
            // listener after cancel() has been called.

            // 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();
            }
        }
    }
}
  • 構(gòu)造函數(shù)
    老規(guī)矩祷安,我們先看一看構(gòu)造函數(shù)姥芥。它有兩個(gè)構(gòu)造函數(shù),如下:
    /**
     * Creates a new response delivery interface.
     *
     * @param handler {@link Handler} to post responses on
     */
    public ExecutorDelivery(final Handler handler)

    /**
     * Creates a new response delivery interface, mockable version for testing.
     *
     * @param executor For running delivery tasks
     */
    public ExecutorDelivery(Executor executor)

區(qū)別是汇鞭,一個(gè)傳入了Handler凉唐,一個(gè)是傳入了Executor。目的是創(chuàng)建一個(gè)Executor霍骄,用于傳遞請(qǐng)求台囱,典型的作用是傳到主線(xiàn)程。Show you the code.

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

看到?jīng)]读整,我們?cè)趧?chuàng)建的Executor的execute方法中簿训,使用handler.post()方式,將runnable放到handler所在線(xiàn)程執(zhí)行绘沉。至于另外一個(gè)構(gòu)造函數(shù)煎楣,就一行代碼,將傳入的參數(shù)賦值給mResponsePoster车伞。

  • ResponseDeliveryRunnable
    一個(gè)用來(lái)將網(wǎng)絡(luò)請(qǐng)求的結(jié)果傳送到在主線(xiàn)程的回調(diào)監(jiān)聽(tīng)者中的Runnable择懂。功能簡(jiǎn)單但很重要,代碼不長(zhǎng)另玖,準(zhǔn)確地詮釋了簡(jiǎn)單卻不簡(jiǎn)單困曙。
    /** A Runnable used for delivering network responses to a listener on the main thread. */
    @SuppressWarnings("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;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // NOTE: If cancel() is called off the thread that we're currently running in (by
            // default, the main thread), we cannot guarantee that deliverResponse()/deliverError()
            // won't be called, since it may be canceled after we check isCanceled() but before we
            // deliver the response. Apps concerned about this guarantee must either call cancel()
            // from the same thread or implement their own guarantee about not invoking their
            // listener after cancel() has been called.

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

從構(gòu)造方法說(shuō)開(kāi),它有三個(gè)參數(shù)谦去,分別是Request慷丽,Response,Runnable鳄哭。作用就是請(qǐng)求信息要糊、響應(yīng)信息和一個(gè)Runnable。這個(gè)方法比較簡(jiǎn)單妆丘,就是分別賦值給內(nèi)部變量锄俄。
然后局劲,最重要的是run()方法。在這個(gè)方法中奶赠,我們進(jìn)行了一些判斷鱼填。首先判斷請(qǐng)求是不是被取消了,如果被取消了毅戈,調(diào)用mRequest.finish("canceled-at-delivery")方法苹丸,結(jié)束請(qǐng)求,并不傳遞到主線(xiàn)程(不產(chǎn)生回調(diào))苇经。
接著判斷得到的響應(yīng)結(jié)果是不是成功的赘理,如果是,則調(diào)用mRequest.deliverResponse()方法扇单,將MResponse.result內(nèi)容傳遞到handler線(xiàn)程感憾。如果沒(méi)有成功,則調(diào)用mRequest.deliverError()將錯(cuò)誤內(nèi)容mResponse.error傳遞到handler線(xiàn)程令花。如果發(fā)現(xiàn)設(shè)置了一個(gè)Runnable阻桅,就調(diào)用它的run()方法。

  • 實(shí)現(xiàn)ResponseDelivery的三個(gè)方法
    @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));
    }

    @Override
    public void postError(Request<?> request, VolleyError error) {
        request.addMarker("post-error");
        Response<?> response = Response.error(error);
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    }

比較簡(jiǎn)單兼都,就不詳細(xì)講解了嫂沉。

NetworkDispatcher

回到RequestQueue的構(gòu)造方法,我們知道扮碧,它還創(chuàng)建了一個(gè)工作線(xiàn)程池趟章。這就需要分析下NetworkDispatcher類(lèi)。先看一下類(lèi)描述慎王。

 Provides a thread for performing network dispatch from a queue of requests.

 <p>Requests added to the specified queue are processed from the network via a specified {@link
 Network} interface. Responses are committed to cache, if eligible, using a specified {@link
 Cache} interface. Valid responses and errors are posted back to the caller via a {@link
 ResponseDelivery}.

意思就是提供用于從請(qǐng)求隊(duì)列執(zhí)行網(wǎng)絡(luò)分派的線(xiàn)程蚓土。所以,這個(gè)類(lèi)是繼承于線(xiàn)程的赖淤。
這個(gè)類(lèi)有一個(gè)構(gòu)造函數(shù)

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

從注釋中蜀漆,我們看到,它用來(lái)創(chuàng)建一個(gè)新的網(wǎng)絡(luò)派發(fā)線(xiàn)程咱旱,你需要調(diào)用start()方法來(lái)讓它運(yùn)行确丢。有四個(gè)參數(shù),分別是:queue吐限,network鲜侥,cache和delivery。queue是傳入的分類(lèi)請(qǐng)求的隊(duì)列诸典;network是用于實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求描函;cache是用于將網(wǎng)絡(luò)響應(yīng)寫(xiě)入緩存;delivery用于遞送響應(yīng)結(jié)果。
再看看線(xiàn)程必須要重寫(xiě)的方法:run()舀寓。

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            try {
                processRequest();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    Thread.currentThread().interrupt();
                    return;
                }
                VolleyLog.e(
                        "Ignoring spurious interrupt of NetworkDispatcher thread; "
                                + "use quit() to terminate it");
            }
        }
    }

首先調(diào)用Process.setThreadPriority將線(xiàn)程設(shè)置為Process.THREAD_PRIORITY_BACKGROUND益缠。在這里列一下Android的線(xiàn)程優(yōu)先級(jí)級(jí)別:

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //設(shè)置線(xiàn)程優(yōu)先級(jí)為后臺(tái),這樣當(dāng)多個(gè)線(xiàn)程并發(fā)后很多無(wú)關(guān)緊要的線(xiàn)程分配的CPU時(shí)間將會(huì)減少基公,有利于主線(xiàn)程的處理,有以下幾種:
int THREAD_PRIORITY_AUDIO //標(biāo)準(zhǔn)音樂(lè)播放使用的線(xiàn)程優(yōu)先級(jí)
int THREAD_PRIORITY_BACKGROUND //標(biāo)準(zhǔn)后臺(tái)程序
int THREAD_PRIORITY_DEFAULT // 默認(rèn)應(yīng)用的優(yōu)先級(jí)
int THREAD_PRIORITY_DISPLAY //標(biāo)準(zhǔn)顯示系統(tǒng)優(yōu)先級(jí)宋欺,主要是改善UI的刷新
int THREAD_PRIORITY_FOREGROUND //標(biāo)準(zhǔn)前臺(tái)線(xiàn)程優(yōu)先級(jí)
int THREAD_PRIORITY_LESS_FAVORABLE //低于favorable
int THREAD_PRIORITY_LOWEST //有效的線(xiàn)程最低的優(yōu)先級(jí)
int THREAD_PRIORITY_MORE_FAVORABLE //高于favorable
int THREAD_PRIORITY_URGENT_AUDIO //標(biāo)準(zhǔn)較重要音頻播放優(yōu)先級(jí)
int THREAD_PRIORITY_URGENT_DISPLAY //標(biāo)準(zhǔn)較重要顯示優(yōu)先級(jí)轰豆,對(duì)于輸入事件同樣適用。

接下來(lái)齿诞,調(diào)用了內(nèi)部方法processRequest()酸休。說(shuō)明:提取到自己的方法,以確保本地有一個(gè)受GC限制的活躍范圍祷杈。這是為了避免在不確定的時(shí)間內(nèi)保持先前的請(qǐng)求引用存活斑司。


    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void addTrafficStatsTag(Request<?> request) {
        // Tag the request (if API >= 14)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
        }
    }

    // Extracted to its own method to ensure locals have a constrained liveness scope by the GC.
    // This is needed to avoid keeping previous request references alive for an indeterminate amount
    // of time. Update consumer-proguard-rules.pro when modifying this. See also
    // https://github.com/google/volley/issues/114
    private void processRequest() throws InterruptedException {
        // Take a request from the queue.
        Request<?> request = mQueue.take();
        processRequest(request);
    }

    @VisibleForTesting
    void processRequest(Request<?> request) {
        // 獲取從設(shè)備boot后經(jīng)歷的時(shí)間值
        long startTimeMs = SystemClock.elapsedRealtime();
        try {
            request.addMarker("network-queue-take");

            // If the request was cancelled already, do not perform the
            // network request.
            if (request.isCanceled()) {
                request.finish("network-discard-cancelled");
                request.notifyListenerResponseNotUsable();
                return;
            }

            addTrafficStatsTag(request);

            // Perform the network request.
            NetworkResponse networkResponse = mNetwork.performRequest(request);
            request.addMarker("network-http-complete");

            // 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");
                request.notifyListenerResponseNotUsable();
                return;
            }

            // Parse the response here on the worker thread.
            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) {
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }

            // Post the response back.
            request.markDelivered();
            mDelivery.postResponse(request, response);
            request.notifyListenerResponseReceived(response);
        } catch (VolleyError volleyError) {
            volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
            parseAndDeliverNetworkError(request, volleyError);
            request.notifyListenerResponseNotUsable();
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
            VolleyError volleyError = new VolleyError(e);
            volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
            mDelivery.postError(request, volleyError);
            request.notifyListenerResponseNotUsable();
        }
    }

    private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
        error = request.parseNetworkError(error);
        mDelivery.postError(request, error);
    }

可以看出來(lái),所有的處理都是在這個(gè)方法中進(jìn)行的:void processRequest(Request<?> request)但汞;
我們一行一行的來(lái)分析宿刮。
第一行割按,取出獲取從設(shè)備boot后經(jīng)歷的時(shí)間值;

long startTimeMs = SystemClock.elapsedRealtime();

第二行貌夕,給request添加一個(gè)Marker。作用是: Adds an event to this request's event log; for debugging. 不重要食绿,所以踩叭,我們不管它磕潮。

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

然后接下來(lái)的幾行的作用是,判斷這個(gè)請(qǐng)求是不是被cancel了容贝。如果是自脯,則不進(jìn)行網(wǎng)絡(luò)請(qǐng)求,同時(shí)發(fā)送本請(qǐng)求不可用通知斤富。

    // If the request was cancelled already, do not perform the
            // network request.
    if (request.isCanceled()) {
        request.finish("network-discard-cancelled");
        request.notifyListenerResponseNotUsable();
        return;
    }

接下來(lái)是為這筆請(qǐng)求設(shè)置流量統(tǒng)計(jì)Tag膏潮。關(guān)于TrafficStats類(lèi)的用法,可以參考這篇文章满力,就不當(dāng)搬運(yùn)工了戏罢。我是任意門(mén)

addTrafficStatsTag(request);

再下來(lái)就是重點(diǎn): 網(wǎng)絡(luò)請(qǐng)求。調(diào)用Network.performRequest()方法完成網(wǎng)絡(luò)請(qǐng)求脚囊,注意這個(gè)是阻塞的龟糕,所以,執(zhí)行完畢之后悔耘,我們就給這個(gè)request設(shè)置了一個(gè)標(biāo)志:network-http-complete讲岁。

    // Perform the network request.
    NetworkResponse networkResponse = mNetwork.performRequest(request);
    request.addMarker("network-http-complete");

然后再判斷是不是上次請(qǐng)求的內(nèi)容沒(méi)有發(fā)生變化(返回代碼304),并且響應(yīng)已經(jīng)被傳遞了,就以“not-modified”結(jié)束缓艳,同時(shí)發(fā)送本請(qǐng)求不可用通知校摩。

    // 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");
        request.notifyListenerResponseNotUsable();
        return;
    }

接下來(lái)是最精彩的地方,我們的網(wǎng)絡(luò)請(qǐng)求結(jié)果就是這樣轉(zhuǎn)換成我們想要的對(duì)象的阶淘。如何實(shí)現(xiàn)的呢衙吩?通過(guò)調(diào)用reques.parseNetworkResponse()。

    // Parse the response here on the worker thread.
    Response<?> response = request.parseNetworkResponse(networkResponse);
    request.addMarker("network-parse-complete");

然后是緩存溪窒。

    // Write to cache if applicable.如果適用坤塞,寫(xiě)入緩存。
    // TODO: Only update cache metadata instead of entire record for 304s.
    if (request.shouldCache() && response.cacheEntry != null) {
        mCache.put(request.getCacheKey(), response.cacheEntry);
        request.addMarker("network-cache-written");
    }

最最最關(guān)鍵的部分來(lái)了~~~ 澈蚌,緩存之后摹芙,我們需要將解析之后的結(jié)果傳送到主線(xiàn)程。首先宛瞄,設(shè)置標(biāo)志浮禾,然后post出去,最后發(fā)送響應(yīng)收到通知份汗。

    // Post the response back.
    request.markDelivered();
    mDelivery.postResponse(request, response);
    request.notifyListenerResponseReceived(response);

--M: 好盈电,打完收工。
--Y: 咦杯活,不對(duì)挣轨,怎么能沒(méi)有錯(cuò)誤處理?
--M: 對(duì)轩猩,你很機(jī)智卷扮。
我們?cè)倏纯串惓!均践?偣瞔atch了兩個(gè)異常晤锹。先看一個(gè)異常處理方法。在這個(gè)方法中彤委,request調(diào)用parseNetworkError解析了網(wǎng)絡(luò)異常鞭铆。然后,調(diào)用Delivery的postError方法焦影,將錯(cuò)誤傳遞到主線(xiàn)程车遂。

private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
    error = request.parseNetworkError(error);
    mDelivery.postError(request, error);
}
  • VolleyError
    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    parseAndDeliverNetworkError(request, volleyError);
    request.notifyListenerResponseNotUsable();
    
    設(shè)置網(wǎng)絡(luò)請(qǐng)求的總時(shí)間,然后翻譯錯(cuò)誤原因并傳遞到主線(xiàn)程斯辰,最后發(fā)送一個(gè)通知舶担。
  • Exception
    VolleyLog.e(e, "Unhandled exception %s", e.toString());
    VolleyError volleyError = new VolleyError(e);
    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
    mDelivery.postError(request, volleyError);
    request.notifyListenerResponseNotUsable();
    
    代碼比較簡(jiǎn)單,根據(jù)Exception創(chuàng)建一個(gè)VolleyError彬呻,然后設(shè)置網(wǎng)絡(luò)請(qǐng)求的總時(shí)間衣陶。然后傳遞到主線(xiàn)程柄瑰。最后發(fā)送一個(gè)通知。

好了剪况,Network類(lèi)解析完成教沾,一個(gè)完整的網(wǎng)絡(luò)請(qǐng)求過(guò)程也捋清楚了。
我們?cè)賮?lái)看一張圖译断,捋一捋Volley的工作流授翻。


紙上得來(lái)終覺(jué)淺,絕知此時(shí)要有圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末孙咪,一起剝皮案震驚了整個(gè)濱河市堪唐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌该贾,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捌臊,死亡現(xiàn)場(chǎng)離奇詭異杨蛋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)理澎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)逞力,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人糠爬,你說(shuō)我怎么就攤上這事寇荧。” “怎么了执隧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵揩抡,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我镀琉,道長(zhǎng)峦嗤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任屋摔,我火速辦了婚禮烁设,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钓试。我一直安慰自己装黑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布弓熏。 她就那樣靜靜地躺著恋谭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挽鞠。 梳的紋絲不亂的頭發(fā)上箕别,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天铜幽,我揣著相機(jī)與錄音,去河邊找鬼串稀。 笑死除抛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的母截。 我是一名探鬼主播到忽,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼清寇!你這毒婦竟也來(lái)了喘漏?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤华烟,失蹤者是張志新(化名)和其女友劉穎翩迈,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盔夜,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡负饲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了喂链。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片返十。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖椭微,靈堂內(nèi)的尸體忽然破棺而出洞坑,到底是詐尸還是另有隱情,我是刑警寧澤蝇率,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布迟杂,位于F島的核電站,受9級(jí)特大地震影響本慕,放射性物質(zhì)發(fā)生泄漏逢慌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一间狂、第九天 我趴在偏房一處隱蔽的房頂上張望攻泼。 院中可真熱鬧,春花似錦鉴象、人聲如沸忙菠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牛欢。三九已至,卻和暖如春淆游,著一層夾襖步出監(jiān)牢的瞬間傍睹,已是汗流浹背隔盛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拾稳,地道東北人吮炕。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像访得,于是被迫代替她去往敵國(guó)和親龙亲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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