Android源碼-Volley

前段時間去面試,發現現在的公司還是挺看重對於底層的理解,一般會問下對於HTTP的理解,這裏就介紹一下Google官方出的一個HTTP框架volley。
volley的使用網上有很多,比如說http://blog.csdn.net/fenghai22/article/details/44061307

Volley

這裏寫圖片描述
Volley框架最主要的是RequestQueue請求隊列,通過Volley.newRequestQueue()方法獲取RequestQueue對象。然後往請求隊列裏面添加各種請求,有緩存隊列和網絡隊列兩個隊列進行處理,當緩存中有該url請求並且這次請求允許緩存時,會直接從緩存中返回,如果不允許或緩存中不存在這個請求url,就會把本次請求拋到網絡隊中進行處理,處理成功或出異常再進行回調。

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
        File cacheDir = new File(context.getCacheDir(), "volley");
        String userAgent = "volley/0";

        try {
            String network = context.getPackageName();
            PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
            userAgent = network + "/" + queue.versionCode;
        } catch (NameNotFoundException var7) {
            ;
        }

        if(stack == null) {
            if(VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
        RequestQueue queue1;
        if(maxDiskCacheBytes <= -1) {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        } else {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1);
        }

        queue1.start();
        return queue1;
    }

new RequestQueue對象的過程首先指定了cache的存儲路徑,然後判斷Android的版本號,new 不同的HttpStack對象。如果大於9就HurlStack,否則HttpClientStack。這兩者的區別就是內部使用HttpURLConnection 和 HttpClient的區別。

HurlStack

public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
        String url = request.getUrl();
        HashMap map = new HashMap();
        map.putAll(request.getHeaders());
        map.putAll(additionalHeaders);
        if(this.mUrlRewriter != null) {
            String parsedUrl = this.mUrlRewriter.rewriteUrl(url);
            if(parsedUrl == null) {
                throw new IOException("URL blocked by rewriter: " + url);
            }

            url = parsedUrl;
        }

        URL parsedUrl1 = new URL(url);
        HttpURLConnection connection = this.openConnection(parsedUrl1, request);
        Iterator responseCode = map.keySet().iterator();

        while(responseCode.hasNext()) {
            String protocolVersion = (String)responseCode.next();
            connection.addRequestProperty(protocolVersion, (String)map.get(protocolVersion));
        }

        setConnectionParametersForRequest(connection, request);
        ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1);
        int responseCode1 = connection.getResponseCode();
        if(responseCode1 == -1) {
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        } else {
            BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage());
            BasicHttpResponse response = new BasicHttpResponse(responseStatus);
            response.setEntity(entityFromConnection(connection));
            Iterator var12 = connection.getHeaderFields().entrySet().iterator();

            while(var12.hasNext()) {
                Entry header = (Entry)var12.next();
                if(header.getKey() != null) {
                    BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
                    response.addHeader(h);
                }
            }

            return response;
        }
    }

使用HttpURLConnection執行HTTP請求的過程是,URL#openConnection()獲取HttpURLConnection對象,對它設置一些連接超時,“https”的設置,http請求頭的配置,當然還有最重要各種請求的請求參數,post請求參數需要重寫Request的getParams()來傳遞,在這裏是通過setConnectionParametersForRequest()中的addBodyIfExists()方法。從getBody()方法中獲取map鍵值對,並轉換爲byte[]作爲OutputStream傳入HttpURLConnection。還有一些其他參數的配置,但這裏請求差不多構建完成,接下來就是獲取response,然後封裝成HttpResponse對象返回。

HttpClientStack

public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
        addHeaders(httpRequest, additionalHeaders);
        addHeaders(httpRequest, request.getHeaders());
        this.onPrepareRequest(httpRequest);
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
        return this.mClient.execute(httpRequest);
    }

使用HttpClient這種請求方式在Android會越來越少,在Android 6.0中已經不建議使用。這裏也是隻有Android 9一下的纔會使用。現在這個市場份額應該不到1%了吧。

上面介紹了兩種請求方式,繼續回到RequestQueue,接下來就是把HttpStack封裝爲BasicNetwork,BasicNetwork的作用其實就是把請求再一次封裝,並且把返回結果再封裝爲NetworkResponse。然後再將DiskBasedCache和BasicNetwork傳入,new 出一個RequestQueue對象。這個就是Volley最最關鍵的請求隊列。最後啓動這個請求隊列。

RequestQueue

啓動

先來看看RequestQueue最關鍵的一些參數

private AtomicInteger mSequenceGenerator;
    private final Map<String, Queue<Request<?>>> mWaitingRequests;
    private final Set<Request<?>> mCurrentRequests;
    //這個是緩存隊列
    private final PriorityBlockingQueue<Request<?>> mCacheQueue;
    //這個是網絡請求隊列
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue;
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    //緩存
    private final Cache mCache;
    //網絡請求
    private final Network mNetwork;
    private final ResponseDelivery mDelivery;
    //網絡請求子線程數組,因爲要多個線程同時請求網絡
    private NetworkDispatcher[] mDispatchers;
    //緩存子線程
    private CacheDispatcher mCacheDispatcher;
    private List<RequestQueue.RequestFinishedListener> mFinishedListeners;

剛纔在volley中已經new 了一個RequestQueue對象,並且調用了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();
        }
    }

先暫停所有已經在運行的線程,然後開啓一個緩存線程CacheDispatcher和n個網絡線程NetworkDispatcher,默認個數是4個。但這裏其實volley的初始化工作算是完成了,接下來就要等用戶把請求添加進來的時候再進行下一步工作。

添加請求

/**
     * Adds a Request to the dispatch queue.
     * @param request The request to service
     * @return The passed-in request
     */
    public Request add(Request request) {
        // Tag the request as belonging to this queue and add it to the set of current requests.
        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;
        }

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

在這裏首先把請求和隊列進行綁定,並且把請求放入mCurrentRequests中,這個set表示的是還未被處理或正在被處理的請求。然後再對request進行編號標記。如果這個request設置爲不需要緩存,就直接將它拋入mNetworkQueue網絡請求的隊列中去。如果需要緩存,就將其拋入mCacheQueue緩存隊列中,前面還可以看到mWaitingRequests,這個是用來對請求的去重,相同請求只有第一次請求的時候會加入緩存隊列,後面只會加入mWaitingRequests中。這樣也就成功把請求放入相應的隊列中去了。接下來就看看它在不同的隊列中是如何被處理的。

NetworkDispatcher網絡線程

先看最簡單的也就是不需要緩存,直接進入網絡線程。

public void run() {
        Process.setThreadPriority(10);

        while(true) {
            Request request;
            while(true) {
                try {
                    request = (Request)this.mQueue.take();
                    break;
                } catch (InterruptedException var4) {
                    if(this.mQuit) {
                        return;
                    }
                }
            }

            try {
                request.addMarker("network-queue-take");
                if(request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                } else {
                    if(VERSION.SDK_INT >= 14) {
                        TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                    }

                    NetworkResponse e = this.mNetwork.performRequest(request);
                    request.addMarker("network-http-complete");
                    if(e.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                        Response response = request.parseNetworkResponse(e);
                        request.addMarker("network-parse-complete");
                        if(request.shouldCache() && response.cacheEntry != null) {
                            this.mCache.put(request.getCacheKey(), response.cacheEntry);
                            request.addMarker("network-cache-written");
                        }

                        request.markDelivered();
                        this.mDelivery.postResponse(request, response);
                    }
                }
            } catch (VolleyError var5) {
                this.parseAndDeliverNetworkError(request, var5);
            } catch (Exception var6) {
                VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
                this.mDelivery.postError(request, new VolleyError(var6));
            }
        }
    }

這個網絡線程首先把自己的優先級設爲最高,然後進入死循環,一直從網絡隊列中去獲取請求,沒有請求就阻塞。獲取到請求後,先判斷它是否已經被取消,如果沒有被取消,接下來版本號大於14就對網絡請求打tag,這個是爲了知道每個網絡請求的數據量以及方便優化耗電量等。接下來就是使用Network進行網絡請求,並且返回了NetworkResponse對象。如果請求返回重定向或者已經被處理過了,就直接結束。否則,對返回結果進行不同請求類型的解析,然後如果它是需要緩存就將它緩存到cache,再將它比較爲已經處理過了,最後再將結果反饋給Request的回調方法。到這裏整一個網絡請求也就走完了。

CacheDispatcher緩存線程

public void run() {
        if(DEBUG) {
            VolleyLog.v("start new dispatcher", new Object[0]);
        }

        Process.setThreadPriority(10);
        this.mCache.initialize();

        while(true) {
            while(true) {
                while(true) {
                    while(true) {
                        try {
                            while(true) {
                                final Request e = (Request)this.mCacheQueue.take();
                                e.addMarker("cache-queue-take");
                                if(e.isCanceled()) {
                                    e.finish("cache-discard-canceled");
                                } else {
                                    Entry entry = this.mCache.get(e.getCacheKey());
                                    if(entry == null) {
                                        e.addMarker("cache-miss");
                                        this.mNetworkQueue.put(e);
                                    } else if(entry.isExpired()) {
                                        e.addMarker("cache-hit-expired");
                                        e.setCacheEntry(entry);
                                        this.mNetworkQueue.put(e);
                                    } else {
                                        e.addMarker("cache-hit");
                                        Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
                                        e.addMarker("cache-hit-parsed");
                                        if(entry.refreshNeeded()) {
                                            e.addMarker("cache-hit-refresh-needed");
                                            e.setCacheEntry(entry);
                                            response.intermediate = true;
                                            this.mDelivery.postResponse(e, response, new Runnable() {
                                                public void run() {
                                                    try {
                                                        CacheDispatcher.this.mNetworkQueue.put(e);
                                                    } catch (InterruptedException var2) {
                                                        ;
                                                    }

                                                }
                                            });
                                        } else {
                                            this.mDelivery.postResponse(e, response);
                                        }
                                    }
                                }
                            }
                        } catch (InterruptedException var4) {
                            if(this.mQuit) {
                                return;
                            }
                        }
                    }
                }
            }
        }
    }

緩存線程也是先把優先級設爲最高,然後對緩存進行初始化。再進入死循環獲取緩存隊列裏面的請求。這裏使用了多個while(true)的嵌套,個人猜想是用來提高這個子線程的存活率。然後也是一樣的判斷請求是否已經取消,再從cache緩存中根據請求url去獲取。如果獲取不到緩存或是緩存已過期,就把這個請求拋到網絡請求隊列mNetworkQueue中去。否則把緩存封裝爲Response,然後返回Request回調。這裏還需要判斷這個緩存是否需要刷新,就是說當前這個緩存還是可以用的,但是最好再請求網絡刷新一下,所以在回調之後又會請求網絡,把緩存數據刷新一下。

好了,到這裏緩存線程也走通了,整個緩存加網絡請求算是理清楚了。現在來整體梳理一下

  • 首先volley框架最關鍵的是他有網絡請求和緩存兩個隊列
  • 有緩存就讀取緩存,沒有緩存就把請求拋到網絡請求隊列中去。
  • 它有四個(默認情況)網絡請求線程在跑,但是沒有對大數據進行特殊處理,所以說它比較適合多個請求併發但請求數據量較小的這種情況。

大概就是就是這些了吧,有什麼不對,希望大家多多指點。

發佈了48 篇原創文章 · 獲贊 37 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章