Volley源碼分析

想必大家都對Volley已經(jīng)很熟悉了吧,google工程師出的這個網(wǎng)絡(luò)框架代碼寫的是真好,值得我們大家去學(xué)習(xí),Volley之所以一出來就受到廣大程序員的歡迎,是因?yàn)樗梢杂梅浅:唵蔚姆椒▉戆l(fā)送Http請求,并且處理服務(wù)器返回的數(shù)據(jù),而且是直接返回到主線程,這樣是不是用起來很爽。

很早之前出的網(wǎng)絡(luò)框架,也很早就研究了,就是一直沒有寫,現(xiàn)在抽空寫出來分享給需要的人,寫的不好的地方請見諒。

Volley的簡單用法請看這個大神的博主Volley的簡單使用

Volley的框架圖
1734948-1f5268ebb67c2be2.png

volley的整個流程其實(shí)很簡單:得到請求添加到緩存請求隊(duì)列中,然后經(jīng)過一系列的判斷,是否緩存中已經(jīng)有此請求,有取出,沒有放入網(wǎng)絡(luò)請求隊(duì)列中 ,最后都是將得到的結(jié)果返回到主線程中。

Volley整個框架是一個典型的生產(chǎn)消費(fèi)者模式 一個消費(fèi)者(CacheDispatcher) 也可以是下一個消費(fèi)者(NetworkDispatcher)的生產(chǎn)者。

接下來我們就從整個流程開始說
1.RequestQueue requestQueue = Volley.newRequestQueue(this);

顧名思義,這一步創(chuàng)建了一個requestQueue請求隊(duì)列。Volley.newRequestQueue(this),別看這個短短的一段代碼,其背后是做了整個volley的準(zhǔn)備工作。(后面我們會細(xì)講)

2.Request

 StringRequest request = new StringRequest("https://www.baidu.com/", new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {

            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {

            }
        });
public abstract class Request<T> implements Comparable<Request<T>>

這個Request是個泛型其中<T> 是你的返回類型。request實(shí)現(xiàn)Comparable這意味著是兩個request之間是可以比較。
Volley官方自帶了一些自己的request

  • StringRequest
  • ImageRequest
  • JsonRequest
  • JsonArrayRequest
  • JsonObjectRequest
  • ClearCacheRequest
    我們也可以根據(jù)需求自己創(chuàng)建Request,在自定義Request的時候我們需要我們實(shí)現(xiàn)兩個抽象類:
void deliverResponse(T response)
Response<T> parseNetworkResponse(NetworkResponse response)
  1. 第一個方法是用來把解析的結(jié)果如何傳遞,一般都是用volley自帶的:Response.Listener<T> 回調(diào)接口傳遞。
    2.第二個方法是用來把NetworkResponse解析成自己想的得到的結(jié)果,比如StringRequest,是轉(zhuǎn)換成Sting,ImageRequest是轉(zhuǎn)換成Bitmap。然后傳給第一個方法deliverResponse。

3. requestQueue.add(request);

接下來說一些這個RequestQueue

public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    } 

創(chuàng)建RequestQueue需要的參數(shù)

  • Cache:Volley使用的持久化磁盤緩存:DiskBasedCache(cacheDir)
  • Network:進(jìn)行發(fā)送網(wǎng)絡(luò)請求的工具。
  • int threadPoolsize:這個是NetworkDispatcher這個線程的數(shù)量。
  • ResponseDelivery:用于傳遞請求結(jié)果的類。
RequestQueue的主要結(jié)構(gòu):
  • Map<String, Queue<Request<?>>> mWaitingRequests :等待請求的集合,一些重復(fù)的請求存放區(qū)域,
  • Set<Request<?>> mCurrentRequests:當(dāng)前請求的集合,包括了所有正在請求的,或者是等待請求的都放在這個集合中
  • PriorityBlockingQueue<Request<?>> mCacheQueue:緩存隊(duì)列。(每一個請求在進(jìn)入隊(duì)列中都會先進(jìn)入這個緩存隊(duì)列)
  • PriorityBlockingQueue<Request<?>> mNetworkQueue:網(wǎng)絡(luò)請求隊(duì)列。
  • AtomicInteger mSequenceGenerator :這個類是給每個Request 生成序列號的。
然后我們來看一下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.
A:
        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        // Process requests in the order they are added.
        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }
B:
        // Insert request into stage if there's already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                // There is already a request in flight. Queue up.
                Queue<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.
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

我在上面這段源碼中標(biāo)注了一下A,和B,我們可以看到使用了兩個同步鎖來往m
CurrentRequests,和mWaitingRequests 存放請求。
A中所有進(jìn)來的請求都添加進(jìn)去了。
B中我們看到mWaitingRequests 的Key是Request的key,Value是request。

我們來詳細(xì)講解一下創(chuàng)建RequestQueue

Volley.newRequestQueue();
這個方法首先會考慮你當(dāng)前的android的api是否>=9
如果大于等于9(3.0) Volley 會選擇使用HttpUrlConnectiion,小于9.0 選擇使用的是HttpClient。Volley分別創(chuàng)建了HurlStack,和HttpClientStack,來封裝這兩個網(wǎng)絡(luò)請求方法。Volley的RequestQueue只要持有Network接口就可以了。所有的網(wǎng)絡(luò)操作都在HttpStack的子類中進(jìn)行的。而NetWork接口中的performRequest方法調(diào)用了HttpSackt.performRequest方法就得到結(jié)果了。

queue.start();

  /**
     * Starts the dispatchers in this queue.
     */
    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();
        }
    }

從代碼中可以看出,這個start方法是開啟了所有的Dispatcher(一個緩存線程,四個網(wǎng)絡(luò)請求線程),stop方法是停止線程的方法。

那么來看一下這兩個Dispatcher:

CacheDispatcher

創(chuàng)建CacheDispatcher所需要的參數(shù):

  • BlockingQueue<Request<?>> cacheQueue :緩存隊(duì)列;
  • BlockingQueue<Request<?>> networkQueue: 網(wǎng)絡(luò)請求隊(duì)列;
  • Cache cache:磁盤緩存隊(duì)列;
  • ResponseDelivery delivery:new ExecutorDelivery(new Handler(Looper.getMainLooper())); 這個類是調(diào)度請求結(jié)果到主線程的,可以看出在創(chuàng)建的時候,創(chuàng)建了一個handler,然后通過handler把結(jié)果發(fā)送到主線程,這就是為什么Volley得到的結(jié)果在主線程的原因(后面會詳細(xì)講解)。創(chuàng)建這個對象是在RequestQueue的構(gòu)造方法中創(chuàng)建的。

CacheDispatcher的run方法:

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

        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
A:
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");
B:
                // If the request has been canceled, don't bother dispatching it.
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }
C:
                // Attempt to retrieve this item from cache.
                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;
                }
D:
                // If it is completely expired, just send it to the network.
                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");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");
E:
                if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the response.
                    mDelivery.postResponse(request, response);
                } else {
                    // Soft-expired cache hit. We can deliver the cached response,
                    // but we need to also send the request to the network for
                    // refreshing.
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;
F:
                    // Post the intermediate response back to the user and have
                    // the delivery then forward the request along to the network.
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }

我把整個代碼分成了幾段:

  • A:可以看出來CacheDispatcher會一直While(true)的運(yùn)行,如果CacheQueue里面沒有Request的話,會被CacheQueue.take()阻塞;
  • B:判斷Request是否已經(jīng)取消了。
  • C: 獲得這個Reuqest在緩存中的結(jié)果。判斷是否為空,為空就把請求放到網(wǎng)絡(luò)請求隊(duì)列里面。
  • D:判斷entry是否已經(jīng)失效了,失效放到網(wǎng)絡(luò)請求隊(duì)列。
    -E:判斷entry需不需要從新請求,不需要的話就直接用過Delivery返回到主線程。需要的話,把它放到網(wǎng)絡(luò)請求線程。

NetworkDispatcher

這個和CacheDispatcher是差不多的就不詳細(xì)寫了,就是通過mNetwork.performRequest(request);進(jìn)行網(wǎng)絡(luò)請求得到結(jié)果,通過mDelivery.postResponse(request, response);發(fā)送到主線程。

ExecutorDelivery

ExecutorDeliver 實(shí)現(xiàn)了ResponseDelivery接口。主要工作就是把得到的結(jié)果傳遞到主線程。

我們來看一下他的構(gòu)造方法:

 /**
     * 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òu)造方法中創(chuàng)建了線程池來管理線程
Eexecutor 是一個異步執(zhí)行框架,將任務(wù)的提交和執(zhí)行解耦,基于生產(chǎn)者-消費(fèi)者模式,其提交任務(wù)的線程相當(dāng)于生產(chǎn)者,執(zhí)行任務(wù)的線程相當(dāng)于消費(fèi)者,用Runnable來表示任務(wù)。

在上面的CacheDispatcher和,NetWorkDispatcher中用到了這兩個方法
A : mDelivery.postResponse(request, response);
B: mDelivery.postResponse(request, response, new Runnable() );
這兩個方法:
A:這個方法其實(shí)調(diào)用的是B方法:

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

B:

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

這個方法中調(diào)用了Delivery的execute方法,此方法的構(gòu)造方法傳入的是Runnable,,在runnable的run方法中調(diào)用了Reuqest的 mRequest.deliverResponse(mResponse.result);最后吧結(jié)果傳到了主線程。

整個Volley的工作流程和源碼分析就結(jié)束了。整個Volley是基于生產(chǎn)者和消費(fèi)者模式。層次清晰,值得借鑒。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容