Volley閱讀筆記

簡(jiǎn)述

Volley是一個(gè)請(qǐng)求封裝框架,之所以說(shuō)是框架,是因?yàn)槠渲饕瞧鸬揭粋€(gè)分工拆分的作用,然后需要根據(jù)自身的使用情況來(lái)進(jìn)行各個(gè)功能組件的實(shí)現(xiàn),從而完成一套完整的網(wǎng)絡(luò)請(qǐng)求流程
接下來(lái)主要分析幾個(gè)主要的組件
基于Volley1.0.19版本

NetworkDispatcher

網(wǎng)絡(luò)調(diào)度者,顧名思義,實(shí)際上就是請(qǐng)求調(diào)度的作用,這個(gè)在Volley中是默認(rèn)實(shí)現(xiàn),接下來(lái)看一下Volley的請(qǐng)求調(diào)度流程

public class NetworkDispatcher extends Thread {
    /** 當(dāng)前請(qǐng)求任務(wù)的隊(duì)列 */
    private final BlockingQueue<Request<?>> mQueue;
    /** 用于實(shí)際發(fā)起網(wǎng)絡(luò)請(qǐng)求的對(duì)象 */
    private final Network mNetwork;
    /** 用于寫(xiě)入請(qǐng)求結(jié)果的緩存 */
    private final Cache mCache;
    /** 用于進(jìn)行請(qǐng)求結(jié)果的分發(fā) */
    private final ResponseDelivery mDelivery;
    /** 當(dāng)前調(diào)度線程是否退出,一旦退出,當(dāng)前線程就會(huì)中斷 */
    private volatile boolean mQuit = false;
    //...

    @Override
    public void run() {
        //設(shè)置線程優(yōu)先級(jí),在Android中這個(gè)優(yōu)先級(jí)相當(dāng)于中等,一般為后臺(tái)線程使用
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request<?> request;
        while (true) {//一個(gè)一直運(yùn)行的線程
            long startTimeMs = SystemClock.elapsedRealtime();
            // release previous request object to avoid leaking request object when mQueue is drained.
            request = null;
            try {
                // 這里通過(guò)使用阻塞隊(duì)列,可以做到空閑的時(shí)候阻塞線程
                // volley默認(rèn)使用的是PriorityBlockingQueue,在take的時(shí)候會(huì)把線程進(jìn)行await
                // 那么下一次有數(shù)據(jù)進(jìn)入隊(duì)列的時(shí)候會(huì)notify再進(jìn)行喚醒
                // 這樣可以很有效的避免線程的輪詢,也可以節(jié)省CPU的開(kāi)銷(xiāo)
                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");

                // 在請(qǐng)求正式開(kāi)始前首先要看一下當(dāng)前請(qǐng)求是否取消
                // 如果取消的話沒(méi)有必要浪費(fèi)網(wǎng)絡(luò)鏈接的流量
                // 注意這里取消后是不會(huì)進(jìn)行Listener的回調(diào)的
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                //通過(guò)預(yù)設(shè)的實(shí)際網(wǎng)絡(luò)請(qǐng)求執(zhí)行者進(jìn)行請(qǐng)求
                //這里通過(guò)接口設(shè)計(jì),具有更好的擴(kuò)展性
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");
                //到這里結(jié)束后,實(shí)際上一次發(fā)往服務(wù)器的請(qǐng)求和接收服務(wù)器響應(yīng)的過(guò)程已經(jīng)完成

                //一個(gè)請(qǐng)求完成了,這時(shí)候可能有兩種情況
                //1.當(dāng)前請(qǐng)求在發(fā)送之前標(biāo)記了cache-control,然后去請(qǐng)求服務(wù)端,然后返回響應(yīng)碼304,表示客戶端本地的緩存數(shù)據(jù)就是最新的
                //那么這樣沒(méi)有必要重復(fù)從網(wǎng)絡(luò)上拉數(shù)據(jù),這個(gè)的具體可以去看http自身的緩存機(jī)制
                //2.當(dāng)前請(qǐng)求已經(jīng)將響應(yīng)結(jié)果進(jìn)行發(fā)送回調(diào),那么沒(méi)有必要重復(fù)進(jìn)行回調(diào)
                //在volley中,request是唯一的
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // 當(dāng)前請(qǐng)求已經(jīng)獲得響應(yīng),那么這里進(jìn)行返回報(bào)文的正體解析
                Response<?> response = request.parseNetworkResponse(networkResponse);
                // 本次請(qǐng)求的響應(yīng)解析已經(jīng)完成
                request.addMarker("network-parse-complete");

                // 1.當(dāng)前請(qǐng)求可以進(jìn)行緩存
                // 2.當(dāng)前緩存的為response中的cacheEntry,這個(gè)需要注意
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // 準(zhǔn)備進(jìn)行結(jié)果的分發(fā),那么這里標(biāo)記已經(jīng)分發(fā),為了避免出現(xiàn)重復(fù)分發(fā)的情況
                request.markDelivered();
                // 將結(jié)果交給分發(fā)者,進(jìn)行結(jié)果的分發(fā)
                // 簡(jiǎn)單的一種情況就是將結(jié)果回調(diào)在主線程或者直接在子線程中處理
                mDelivery.postResponse(request, response);

                //出現(xiàn)異常的時(shí)候,比方說(shuō)網(wǎng)絡(luò)請(qǐng)求一個(gè)找不到的域名之類(lèi)的
                //需要進(jìn)行異常的回調(diào),從而通知?jiǎng)e人進(jìn)行處理
            } catch (VolleyError volleyError) {
                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);
            }
        }
    }
    //...

1.NetworkDispatcher實(shí)際上就是一個(gè)線程,在線程中進(jìn)行無(wú)限循環(huán),不斷地從隊(duì)列中獲取請(qǐng)求進(jìn)行處理
2.為了避免真正意義上的無(wú)限輪詢,應(yīng)該結(jié)合線程的掛起和喚醒機(jī)制進(jìn)行處理,這樣可以靈活做到?jīng)]有任務(wù)的時(shí)候掛起,有任務(wù)到來(lái)的時(shí)候喚醒并且執(zhí)行,這里通過(guò)BlockingQueue作為任務(wù)隊(duì)列的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)這個(gè)效果
3.其實(shí)從NetworkDispatcher中可以看到幾個(gè)任務(wù)的分工,比方說(shuō)緩存、請(qǐng)求發(fā)起者、結(jié)果分發(fā)者,這些在后面會(huì)細(xì)說(shuō)。
這里主要是能夠看出整個(gè)Volley的基礎(chǔ)任務(wù)流程
1.從隊(duì)列中獲取請(qǐng)求
2.檢查當(dāng)前請(qǐng)求是否取消,如果請(qǐng)求已經(jīng)被取消,不進(jìn)行回調(diào)并且善后處理。如果請(qǐng)求沒(méi)有取消,繼續(xù)步驟3
3.通過(guò)request發(fā)起網(wǎng)絡(luò)請(qǐng)求
4.如果當(dāng)前請(qǐng)求的響應(yīng)為本地緩存已經(jīng)是最新,那么此時(shí)可以直接使用本地緩存中的數(shù)據(jù),那么也就不需要進(jìn)行
5.解析服務(wù)器返回的報(bào)文中的body部分
6.當(dāng)前請(qǐng)求是否可以緩存,如果可以緩存,將可以緩存的部分存入緩存中
7.通過(guò)回調(diào)分發(fā)者進(jìn)行分發(fā),最后進(jìn)行回調(diào)(上述步驟中的任何異常都會(huì)進(jìn)行異常回調(diào))

Network

實(shí)際進(jìn)行網(wǎng)絡(luò)請(qǐng)求操作的對(duì)象,通過(guò)接口的模式擴(kuò)展,可以根據(jù)自身的情況實(shí)現(xiàn)不同的請(qǐng)求,比方說(shuō)可以用OkHttp、HttpURLConnection來(lái)做底層的網(wǎng)絡(luò)請(qǐng)求實(shí)現(xiàn)
在Volley中有一個(gè)默認(rèn)的實(shí)現(xiàn)

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

        Network network = new BasicNetwork(stack);

注意到這里還分了兩層,其中一層就是Network,但是看到默認(rèn)實(shí)現(xiàn)為BasicNetwork
,內(nèi)部主要是做一些默認(rèn)操作,比方說(shuō)處理響應(yīng)碼等操作

    @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 {
                Map<String, String> headers = new HashMap<String, String>();
                //這里是在請(qǐng)求之前設(shè)置緩存相關(guān)的頭部
                //比方說(shuō)If-Modified-Since,這樣的意義是讓服務(wù)器判斷當(dāng)前本地緩存是否過(guò)期
                addCacheHeaders(headers, request.getCacheEntry());
                //這里可以看到,實(shí)際的請(qǐng)求交給了HttpStack處理
                httpResponse = mHttpStack.performRequest(request, headers);
                //成功獲得響應(yīng),這里是獲得請(qǐng)求的響應(yīng)碼
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();
                //獲得響應(yīng)的頭部報(bào)文
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                //當(dāng)前響應(yīng)碼為304,說(shuō)明當(dāng)前客戶端本地緩存已經(jīng)是最新數(shù)據(jù)
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                    //因?yàn)榉?wù)端返回的響應(yīng)碼為304,這意味著客戶端緩存就是最新數(shù)據(jù),并且當(dāng)前服務(wù)端不會(huì)返回?cái)?shù)據(jù)
                    //那么此時(shí)需要手動(dòng)構(gòu)建響應(yīng)體,從而做到統(tǒng)一的請(qǐng)求響應(yīng)完成
                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }

                    entry.responseHeaders.putAll(responseHeaders);
                    //這里的entry.data其實(shí)就是本地緩存中的數(shù)據(jù)
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }
                
                // 請(qǐng)求返回重定向的響應(yīng)碼
                if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    //重定向的鏈接默認(rèn)規(guī)定是在響應(yīng)的頭部報(bào)文的Location字段中,這個(gè)是Http協(xié)議規(guī)定的
                    String newUrl = responseHeaders.get("Location");
                    request.setRedirectUrl(newUrl);
                }

                // 一些響應(yīng)碼比方說(shuō)204雖然也是成功,但是響應(yīng)報(bào)文中是沒(méi)有body的
                if (httpResponse.getEntity() != null) {
                    //網(wǎng)絡(luò)連接實(shí)際上都是一個(gè)socket的連接
                    //然后客戶端和服務(wù)端分別打開(kāi)輸入和輸出流進(jìn)行數(shù)據(jù)交換
                    //這里就是將服務(wù)端向客戶端輸入的輸入流轉(zhuǎn)換為字節(jié)數(shù)組
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // 這里只是默認(rèn)非null而已
                  responseContents = new byte[0];
                }

                // 這里的時(shí)間為請(qǐng)求發(fā)出和客戶端收到響應(yīng)的時(shí)間
                // 一般會(huì)包括請(qǐng)求發(fā)出的延時(shí)、網(wǎng)絡(luò)連接的延時(shí)(DNS解析之類(lèi)的)、服務(wù)端處理延時(shí)等
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    //這里實(shí)際上已經(jīng)處理完了304響應(yīng)碼,304已經(jīng)返回了自己拼裝的數(shù)據(jù)
                    //這里拋出異常有兩種情況,一個(gè)
                    throw new IOException();
                }
                //這里是響應(yīng)成功的正常數(shù)據(jù)
                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 = 0;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) {
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                } else {//絕大多數(shù)出現(xiàn)異常的時(shí)候會(huì)在這里拋出異常,從而進(jìn)行異常處理回調(diào)
                    throw new NoConnectionError(e);
                }
                if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 
                        statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl());
                } else {
                    VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                }
                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 == HttpStatus.SC_MOVED_PERMANENTLY || 
                                statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                        //重定向處理,但是現(xiàn)在DefaultRetryPolicy中是沒(méi)有處理的
                        attemptRetryOnException("redirect",
                                request, new RedirectError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(e);
                }
            }
        }
    }

稍微總結(jié)一下,在BasicNetwork中主要是封裝了緩存的頭部和一些默認(rèn)的請(qǐng)求完成之后的處理,從很大程度上可以降低他人實(shí)現(xiàn)的成本,那么在外部主要是實(shí)現(xiàn)HttpStack即可
這里以Volley默認(rèn)實(shí)現(xiàn)的HurlStack為例

    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        //在這里之前,BasicNetwork封裝了緩存的一些頭部報(bào)文
        String url = request.getUrl();
        HashMap<String, String> map = new HashMap<String, String>();
        map.putAll(request.getHeaders());//設(shè)置請(qǐng)求預(yù)設(shè)的頭部報(bào)文
        map.putAll(additionalHeaders);//為了避免覆蓋,所以BasicNetwork的頭部報(bào)文后設(shè)置
        if (mUrlRewriter != null) {
            //這里主要是提供一個(gè)機(jī)會(huì)修改請(qǐng)求的實(shí)際url
            String rewritten = mUrlRewriter.rewriteUrl(url);
            if (rewritten == null) {
                throw new IOException("URL blocked by rewriter: " + url);
            }
            url = rewritten;
        }
        URL parsedUrl = new URL(url);
        //這里就是創(chuàng)建一個(gè)連接
        //1.設(shè)置連接和讀取超時(shí)
        //2.如果當(dāng)前是Https請(qǐng)求,允許設(shè)置SSL認(rèn)證工廠
        HttpURLConnection connection = openConnection(parsedUrl, request);
        for (String headerName : map.keySet()) {//將之前設(shè)置的請(qǐng)求頭部按照http協(xié)議的格式設(shè)置
            connection.addRequestProperty(headerName, map.get(headerName));
        }
        //根據(jù)請(qǐng)求方式,常用的比方說(shuō)POST
        //在請(qǐng)求報(bào)文中設(shè)置請(qǐng)求方式為POST
        //然后通過(guò)socket連接建立的通路中的OutputStream將請(qǐng)求參數(shù)從客戶端輸出到服務(wù)器中
        setConnectionParametersForRequest(connection, request);
        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        int responseCode = connection.getResponseCode();//獲得服務(wù)器的響應(yīng)碼
        if (responseCode == -1) {
            // -1 is returned by getResponseCode() if the response code could not be retrieved.
            // Signal to the caller that something was wrong with the connection.
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        }
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);
        if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
            //這里進(jìn)行響應(yīng)體的數(shù)據(jù)綁定
            //包括返回?cái)?shù)據(jù)的流、返回?cái)?shù)據(jù)格式和大小等等
            response.setEntity(entityFromConnection(connection));
        }
        for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
            if (header.getKey() != null) {//添加響應(yīng)頭部
                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                response.addHeader(h);
            }
        }
        //實(shí)際上在HttpStack中主要做了這樣的幾件事情
        //1.實(shí)際發(fā)出請(qǐng)求
        //2.根據(jù)相應(yīng)結(jié)果進(jìn)行response的包裝
        return response;
    }

這個(gè)只是Volley的一個(gè)默認(rèn)實(shí)現(xiàn),在實(shí)際使用中需要根據(jù)自己的需要來(lái)實(shí)現(xiàn)。

ResponseDelivery

當(dāng)一次請(qǐng)求獲得響應(yīng)之后,那么接下來(lái)就要進(jìn)行結(jié)果的發(fā)送和回調(diào),這個(gè)工作就是通過(guò)實(shí)現(xiàn)ResponseDelivery來(lái)完成
這個(gè)主要看接口設(shè)計(jì)就可以明白了

public interface ResponseDelivery {
    /**
     * 默認(rèn)的使用是這個(gè)方法,用于響應(yīng)網(wǎng)絡(luò)或者緩存獲得的響應(yīng)
     */
    public void postResponse(Request<?> request, Response<?> response);

    /**
     * 后面提供了這個(gè)方法,在postResponse(Request<?> request, Response<?> response)的基礎(chǔ)上
     * 添加了一個(gè)runnable用于在響應(yīng)回調(diào)之后進(jìn)行一個(gè)默認(rèn)處理
     */
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable);

    /**
     * 用于發(fā)送異常回調(diào)
     */
    public void postError(Request<?> request, VolleyError error);
}

實(shí)際上就是用于發(fā)送成功響應(yīng)的響應(yīng)體或者發(fā)送請(qǐng)求失敗的異常,接下來(lái)看一下默認(rèn)的實(shí)現(xiàn)ExecutorDelivery

    public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {//實(shí)際上就是把任務(wù)扔進(jìn)handler中執(zhí)行,所以說(shuō)重點(diǎn)在handler的執(zhí)行線程
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

    @Override
    public void run() {
        // 在發(fā)送之前先檢查當(dāng)前任務(wù)是否取消
        if (mRequest.isCanceled()) {
            //任務(wù)取消則不會(huì)進(jìn)行回調(diào)發(fā)送
            mRequest.finish("canceled-at-delivery");
            return;
        }

        //實(shí)際上就是在這里進(jìn)行回調(diào)處理
        //在默認(rèn)的場(chǎng)景下,發(fā)出的每一個(gè)請(qǐng)求都需要定義一個(gè)回調(diào)
        //那么就是通過(guò)實(shí)現(xiàn)Request中的對(duì)應(yīng)方法實(shí)現(xiàn)
        if (mResponse.isSuccess()) {
            mRequest.deliverResponse(mResponse.result);
        } else {
            mRequest.deliverError(mResponse.error);
        }

        //此時(shí)回調(diào)已經(jīng)完成

        if (mResponse.intermediate) {
            //這個(gè)標(biāo)志意味著當(dāng)前請(qǐng)求沒(méi)有完成,此時(shí)有點(diǎn)尷尬
            //默認(rèn)的情況中在NetworkDispatcher中的任務(wù)隊(duì)列中已經(jīng)移除
            //但是此時(shí)在RequestQueue中的當(dāng)前請(qǐng)求隊(duì)列中沒(méi)有移除
            //這個(gè)目前的應(yīng)用在CacheDispatcher中,成功從緩存中獲取數(shù)據(jù),然后先進(jìn)行回調(diào)
            //之后再通過(guò)runnable將當(dāng)前請(qǐng)求放到任務(wù)隊(duì)列中,用于緩存處理后再次發(fā)送請(qǐng)求
            mRequest.addMarker("intermediate-response");
        } else {
            //正常的一次任務(wù)完成之后進(jìn)行收尾處理
            mRequest.finish("done");
        }

        // 目前的使用場(chǎng)景是CacheDispatcher,用于在緩存處理之后再次發(fā)送請(qǐng)求
        if (mRunnable != null) {
            mRunnable.run();
        }
    }

在默認(rèn)的設(shè)計(jì)上,設(shè)計(jì)者是希望開(kāi)發(fā)者直接通過(guò)不同線程的Handler,默認(rèn)是主線程的Handler來(lái)進(jìn)行回調(diào)處理。
這個(gè)個(gè)人認(rèn)為還是要按照實(shí)際的場(chǎng)景來(lái)處理,因?yàn)橛械臅r(shí)候一個(gè)請(qǐng)求回來(lái)了可能會(huì)直接接入一個(gè)耗時(shí)操作,可能此時(shí)直接在其它線程池中執(zhí)行相對(duì)合理。

Cache

說(shuō)到緩存,首先就是要從Volley默認(rèn)提供的CacheDispatcher來(lái)時(shí)說(shuō)起,同NetworkDispatcher差不多的意思,只是職責(zé)是處理緩存相關(guān)事宜。

    @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) {
            request = null;
            try {
                // 從緩存請(qǐng)求隊(duì)列中獲取請(qǐng)求,這個(gè)同樣會(huì)阻塞
                request = mCacheQueue.take();
            } catch (InterruptedException e) {
                if (mQuit) {
                    return;
                }
                continue;
            }
            try {
                request.addMarker("cache-queue-take");

                if (request.isCanceled()) {//當(dāng)前請(qǐng)求已經(jīng)被取消,不需要繼續(xù)從緩存中獲取數(shù)據(jù)等一系列操作
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // 根據(jù)緩存的鍵名從緩存中獲取數(shù)據(jù)
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {//未能命中緩存
                    request.addMarker("cache-miss");
                    // 將當(dāng)前任務(wù)添加當(dāng)網(wǎng)絡(luò)請(qǐng)求隊(duì)列中,那么后續(xù)就會(huì)進(jìn)入NetworkDispatcher中執(zhí)行網(wǎng)絡(luò)請(qǐng)求
                    mNetworkQueue.put(request);
                    continue;
                }

                //在http緩存機(jī)制中緩存有兩種過(guò)期
                //首先一個(gè)響應(yīng)一定有一個(gè)截止時(shí)間
                //但是有個(gè)響應(yīng)可能允許額外的延長(zhǎng)過(guò)期時(shí)間
                //這個(gè)具體可以看cache-control
                //在Volley中分別被封裝成了entry中的
                //softTtl和ttl

                if (entry.isExpired()) {//當(dāng)前時(shí)間已經(jīng)大于ttl,那么緩存一定是過(guò)期的,這個(gè)時(shí)間可能包括額外的延長(zhǎng)時(shí)間
                    //雖然成功擊中緩存,但是當(dāng)前緩存已經(jīng)過(guò)期
                    //此時(shí)還是去發(fā)起網(wǎng)絡(luò)請(qǐng)求
                    request.addMarker("cache-hit-expired");
                    //也許后續(xù)服務(wù)器會(huì)返回304標(biāo)記數(shù)據(jù)沒(méi)有變化,此時(shí)可以直接使用之前的entry
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }
                //當(dāng)前擊中緩存,并且緩存數(shù)據(jù)沒(méi)有完全過(guò)期
                request.addMarker("cache-hit");
                //通過(guò)緩存的結(jié)果構(gòu)建response,主要是用于后續(xù)的統(tǒng)一回調(diào)處理
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                //當(dāng)前沒(méi)有完全過(guò)期
                if (!entry.refreshNeeded()) {//并且當(dāng)前還沒(méi)有到緩存過(guò)期的時(shí)間
                    //直接使用緩存結(jié)果進(jìn)行回調(diào)處理即可
                    mDelivery.postResponse(request, response);
                } else {//當(dāng)前已經(jīng)超過(guò)緩存過(guò)期的時(shí)間,不過(guò)卻在額外延長(zhǎng)的時(shí)間內(nèi)
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    response.intermediate = true;

                    final Request<?> finalRequest = request;
                    //此時(shí)還是認(rèn)為緩存有效并且進(jìn)行回調(diào)
                    //但是回調(diào)之后還會(huì)發(fā)出網(wǎng)絡(luò)請(qǐng)求,用于刷新緩存數(shù)據(jù)
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                //回調(diào)處理緩存之后,再次發(fā)起網(wǎng)絡(luò)請(qǐng)求
                                mNetworkQueue.put(finalRequest);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
            }
        }
    }

1.從緩存隊(duì)列中獲取請(qǐng)求,這些添加到隊(duì)列中的請(qǐng)求肯定是允許使用緩存的
2.從緩存中獲取數(shù)據(jù)
3.如果沒(méi)有擊中緩存或者緩存完全過(guò)期,將當(dāng)前任務(wù)放入網(wǎng)絡(luò)請(qǐng)求隊(duì)列中等待執(zhí)行網(wǎng)絡(luò)請(qǐng)求,否則繼續(xù)步驟4
4.當(dāng)前緩存中的數(shù)據(jù)還沒(méi)有到過(guò)期時(shí)間,直接進(jìn)行回調(diào),完成本次請(qǐng)求。如果已經(jīng)超過(guò)過(guò)期時(shí)間,但是還在延長(zhǎng)時(shí)間內(nèi),此時(shí)先進(jìn)行緩存數(shù)據(jù)的回調(diào),然后再將任務(wù)放入網(wǎng)絡(luò)請(qǐng)求隊(duì)列中等待執(zhí)行網(wǎng)絡(luò)請(qǐng)求來(lái)進(jìn)行緩存的刷新和回調(diào)操作
看到這里,基本上的組件已經(jīng)介紹完畢,最后從入口開(kāi)始看一下基礎(chǔ)流程

Volley

作為整個(gè)框架的入口,實(shí)際上就是一個(gè)工廠,里面會(huì)提供一些默認(rèn)選項(xiàng),這里就看默認(rèn)的提供

    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) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();//一般都是這里,默認(rèn)使用
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //對(duì)請(qǐng)求執(zhí)行者再包裝一層
        Network network = new BasicNetwork(stack);
        
        RequestQueue queue;
        //創(chuàng)建請(qǐng)求隊(duì)列管理對(duì)象
        if (maxDiskCacheBytes <= -1) {
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        } else {//具有自定義的最大硬盤(pán)緩存大小
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }
        //開(kāi)始請(qǐng)求隊(duì)列的運(yùn)作
        queue.start();

        return queue;
    }

可以看到,實(shí)際上RequestQueue才是執(zhí)行者,在Volley中主要是初始化硬盤(pán)緩存的目錄,確定請(qǐng)求的執(zhí)行者,初始化請(qǐng)求隊(duì)列管理,并且最終開(kāi)啟隊(duì)列的運(yùn)作,這意味著Volley開(kāi)始運(yùn)作。

RequestQueue

這個(gè)其實(shí)才是Volley對(duì)外的操作對(duì)象,默認(rèn)提供一個(gè)addRequest操作,用于添加一個(gè)請(qǐng)求,從之前的講述可以看到內(nèi)部至少有CacheDispatcher和NetworkDispatcher在工作,看一下處理邏輯:

    public void start() {
        stop();
        //實(shí)際上在volley的架構(gòu)中
        //操作都是通過(guò)Dispatcher進(jìn)行的
        //不同的Dispatcher的職責(zé)不同
        //默認(rèn)使用的只有緩存相關(guān)和請(qǐng)求相關(guān)
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        //實(shí)際上NetworkDispatcher本身就是一個(gè)線程Thread
        //默認(rèn)使用的線程固定為4個(gè)
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

    public <T> Request<T> add(Request<T> request) {

        request.setRequestQueue(this);
        synchronized (mCurrentRequests) {//將請(qǐng)求添加到當(dāng)前請(qǐng)求隊(duì)列,這個(gè)主要是用于停止請(qǐng)求的
            mCurrentRequests.add(request);
        }

        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        // 當(dāng)前請(qǐng)求不使用緩存
        if (!request.shouldCache()) {
            //將當(dāng)前請(qǐng)求放入請(qǐng)求隊(duì)列中,等待NetworkDispatcher獲取后執(zhí)行網(wǎng)絡(luò)請(qǐng)求
            mNetworkQueue.add(request);
            return request;
        }
        // 當(dāng)前請(qǐng)求需要使用緩存

        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();//獲取緩存的鍵名
            if (mWaitingRequests.containsKey(cacheKey)) {
                //當(dāng)前已經(jīng)有同樣的緩存請(qǐng)求在進(jìn)行中
                //將本次請(qǐng)求添加到等待隊(duì)列中
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request<?>>();
                }
                stagedRequests.add(request);
                //這個(gè)會(huì)等待最早的一個(gè)請(qǐng)求成功之后再處理,具體邏輯在finish中
                //簡(jiǎn)單說(shuō)就是最早的一個(gè)請(qǐng)求成功了,然后再將之前等待的任務(wù)全部放入緩存請(qǐng)求隊(duì)列中
                //最好當(dāng)然是全部擊中緩存
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // 用于標(biāo)記當(dāng)前請(qǐng)求進(jìn)行中
                mWaitingRequests.put(cacheKey, null);
                // 將任務(wù)放入緩存請(qǐng)求隊(duì)列中,等待CacheDispatcher獲取后執(zhí)行
                mCacheQueue.add(request);
            }
            return request;
        }
    }
    
    <T> void finish(Request<T> request) {
        
        synchronized (mCurrentRequests) {//當(dāng)前請(qǐng)求已經(jīng)完成,從當(dāng)前請(qǐng)求中隊(duì)列移除
            mCurrentRequests.remove(request);
        }
        synchronized (mFinishedListeners) {//進(jìn)行請(qǐng)求完成回調(diào)
            for (RequestFinishedListener<T> listener : mFinishedListeners) {
                listener.onRequestFinished(request);
            }
        }
        //處理那些等待緩存的請(qǐng)求
        if (request.shouldCache()) {
            synchronized (mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
                if (waitingRequests != null) {
                    if (VolleyLog.DEBUG) {
                        VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
                                waitingRequests.size(), cacheKey);
                    }
                    //將之前等待的任務(wù)全部放入緩存請(qǐng)求隊(duì)列中,后續(xù)最好是全部擊中緩存
                    mCacheQueue.addAll(waitingRequests);
                }
            }
        }
    }

簡(jiǎn)單看一下添加請(qǐng)求的邏輯
1.判斷請(qǐng)求是否使用http緩存,如果不使用,添加到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中等待NetworkDispatcher獲取后執(zhí)行,否則繼續(xù)步驟2
2.當(dāng)前使用緩存,那么判斷當(dāng)前請(qǐng)求是否有同樣的緩存請(qǐng)求已經(jīng)在發(fā)出中,如果沒(méi)有,將當(dāng)前請(qǐng)求加到緩存請(qǐng)求隊(duì)列中等待CacheDispatcher獲取后執(zhí)行,否則繼續(xù)步驟3
3.添加到等待隊(duì)列中,等待最早的一個(gè)相同的緩存請(qǐng)求完成之后再添加到緩存請(qǐng)求隊(duì)列中,此時(shí)有很大概率可擊中緩存

結(jié)語(yǔ)

實(shí)際上Volley還有下載圖片等功能,不過(guò)可能比起正牌的圖片框架Glide之類(lèi)的還是要弱上不少,基于篇幅限制,還有更多默認(rèn)的功能沒(méi)有講。
主要還是希望能夠理解Volley對(duì)于請(qǐng)求分發(fā)框架的設(shè)計(jì),無(wú)論是對(duì)于直接使用還是寫(xiě)自己的請(qǐng)求框架都是有很大的益處的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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