Volley實現脈絡學習

JamFelician 8年前發布 | 6K 次閱讀 Volley Android開發 移動開發

Volley 是 Google 推出的 Android 異步網絡請求框架和圖片加載框架。在 Google I/O 2013 大會上發布的,現在連android系統中內部也是使用了Volley作為其網絡的請求框架,谷歌出品,必屬精品,所以有必要進行一次梳理其實現的總體過程,學習其設計框架的思路也是必要的(文章適合知道基本使用Volley的同學觀看)。

Volley

一般我們都會在app初始化的時候調用如下代碼進行初始化Volley,下面就從該方法當做閱讀入口進行逐一跟蹤:

mRequestQueue =  Volley.newRequestQueue(this);

在調用Volley.newRequestQueue(this)時,最終調用到如下代碼:

/**
     * 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.
     *HttpStack是用來進行網絡請求封裝的類,傳入null代表默認
     *maxDiskCacheBytes為最大磁盤緩存,傳入-1使用默認大小
     */
    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) {
        }
        //我們可以傳入okttp來充當我們的httpStack
        if (stack == null) {
          //使用Volley默認封裝的stack,依靠android版本來決定使用不同的connection來進行網絡請求
            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);

        RequestQueue queue;
        if (maxDiskCacheBytes <= -1)
        {
            // No maximum size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        }
        else
        {
            // Disk cache size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }

        queue.start();

        return queue;
    }

在newRequestQueue方法中進行了相關必要參數的初始化的工作,其中包括:

  • 緩存區域的初始化
  • HttpStack的初始化
  • Network的初始化
  • RequestQueue的初始化,最終調用其start()方法

Stack 詳解

在Volley中如果沒有傳遞任何HttpStack則會使用了兩種stack來進行不同android版本(SDK_INT >= 9?HurlStack:HttpClientStack)的適配工作:HurlStack和HttpClientStack,兩種stack都實現了HttpStack接口,接口定義如下:

public interface HttpStack {
    //請求處理方法
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError;

}

在接口中定義performRequest方法,在其子類中實現了對于http請求的封裝的操作,以此來查看對應實現子類的performRequest方法。

在HurlStack中關于performRequest(…)的實現:

@Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        String url = request.getUrl();
      //存放請求頭
        HashMap<String, String> map = new HashMap<String, String>();
        map.putAll(request.getHeaders());
        map.putAll(additionalHeaders);
      //是否重寫url
        if (mUrlRewriter != null) {
            String rewritten = mUrlRewriter.rewriteUrl(url);
            if (rewritten == null) {
                throw new IOException("URL blocked by rewriter: " + url);
            }
            url = rewritten;
        }
        URL parsedUrl = new URL(url);
      //使用后HttpURLConnection進行網絡請求
        HttpURLConnection connection = openConnection(parsedUrl, request);
        for (String headerName : map.keySet()) {
            connection.addRequestProperty(headerName, map.get(headerName));
        }
      //設置請求方法:GET ,POST, ect
        setConnectionParametersForRequest(connection, request);
        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        int responseCode = connection.getResponseCode();
        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);
          //將返回的數據流寫入response
        response.setEntity(entityFromConnection(connection));
          //獲取返回的請求頭
        for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
            if (header.getKey() != null) {
                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                response.addHeader(h);
            }
        }
        return response;
    }

HurlStack使用HttpURLConnection進行Http請求的過程,步驟是基本的網絡請求的過程,設置請求頭->包裝Url->打開連接->設置請求方法->獲取數據已經請求狀態->將數據流存入HttpResponse,最終返回一個HttpResponse。

HttpClientStack中關于performRequest(…)的實現:

@Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
          //構造請求參數
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
          //添加請求頭
        addHeaders(httpRequest, additionalHeaders);
        addHeaders(httpRequest, request.getHeaders());
        onPrepareRequest(httpRequest);
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        // TODO: Reevaluate this connection timeout based on more wide-scale
        // data collection and possibly different for wifi vs. 3G.
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
        return mClient.execute(httpRequest);
    }

HttpClientStack執行的大體過程與HurlStack基本上一致,通過一系列操作最終調用AndroidHttpClient.execute(httpRequest)返回一個HttpResponse。

HttpStack封裝了http請求的過程,并且只管http請求,滿足設計模式中單一責任的原則,并且向外提供接口HttpStack方便使用者定制自身的HttpStack類來進行請求的過程,如:可以用OkhttpClient來進行http請求的過程,這對于Volley的整個流程是不受影響。

Network

public interface Network {
    /**
     * Performs the specified request.
     * @param request Request to process
     * @return A {@link NetworkResponse} with data and caching metadata; will never be null
     * @throws VolleyError on errors
     */
    public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}

Network接口中定義了performRequest用于接收一個具體的Request請求,其實現類是BasicNetwork,我們直接查看performRequest方法:

@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());
                  //通過HttpStack獲得HttpResponse
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();
                //返回的請求頭
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                //請求返回的狀態碼處理
                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);
                    }

                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // Handle moved resources
                if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    String newUrl = responseHeaders.get("Location");
                    request.setRedirectUrl(newUrl);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                }

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                        SystemClock.elapsedRealtime() - requestStart);
                ...
                int statusCode = 0;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) {
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                } else {
                    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);
                    ...
            }
        }
    }

在BasicNetwork中,我們可以看到其通過寫了一個死循環來保證重復請求的操作直到報錯或者返回結果,在其中他會調用mHttpStack.performRequest(request, headers)來獲得一個HttpResponse,調用entityToBytes()將entity轉換成一個字節數組,并且獲取相關參數(header,statusCode .etc)去進一步的構造出一個NetworkResponse,并且返回:

public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
            boolean notModified, long networkTimeMs) {
        this.statusCode = statusCode;
        this.data = data;
        this.headers = headers;
        this.notModified = notModified;
        this.networkTimeMs = networkTimeMs;
    }

    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
            boolean notModified) {
        this(statusCode, data, headers, notModified, 0);
    }

    public NetworkResponse(byte[] data) {
        this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false, 0);
    }

    public NetworkResponse(byte[] data, Map<String, String> headers) {
        this(HttpStatus.SC_OK, data, headers, false, 0);
    }

    /**Http狀態碼 */
    public final int statusCode;

    /**網絡請求返回的字節數據 */
    public final byte[] data;

    /**返回的請求頭 */
    public final Map<String, String> headers;

    /** 是否返回的304的狀態碼*/
    public final boolean notModified;

    /** 請求耗時. */
    public final long networkTimeMs;

RequestQueue

RequestQueue是一個請求隊列,Volley將所有Request請求維護在此請求對列當中,內部有兩個對應緩存和網絡的請求阻塞隊列來對Request進行維護,并且通過不同的Dispatcher(extends Thread)進行進一步的請求結果的處理,主要代碼如下:

public class RequestQueue {

    /** 所有請求完成后的回調 */
    public static interface RequestFinishedListener<T> {
        /** Called when a request has finished processing. */
        public void onRequestFinished(Request<T> request);
    }
    ...
      //等待請求的Request,如果一個請求可以進行緩存,則后續的相同CacheKey的請求,將進入此等待隊列。
    private final Map<String, Queue<Request<?>>> mWaitingRequests =
            new HashMap<String, Queue<Request<?>>>();
      //當前請求以及未完成的請求
    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
    /** 緩存請求阻塞隊列 */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<Request<?>>();
    /** 網絡請求阻塞隊列 */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();
      //網絡請求線程池大小
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
    ...

      //網絡dispatcher數組
    private NetworkDispatcher[] mDispatchers;

    //緩存dispatcher
    private CacheDispatcher mCacheDispatcher;
    //Request完成listener
    private List<RequestFinishedListener> mFinishedListeners =
            new ArrayList<RequestFinishedListener>();

    public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
          //初始化網絡Dispatcher
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }
    /**
     * start()方法中啟動了緩存dispatcher跟網絡請求dispatcher,用來處理數據請求,
     * 如果緩存中有數據,則直接在緩存中獲取數據,緩存中沒有數據則從網絡中獲取數據。
     */
    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // 啟動緩存請求隊列
        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();
        }
    }
    ...

    /**
     * 添加新的請求到請求對列當中
     */
    public <T> Request<T> add(Request<T> request) {
        ...
    }

    ...
    <T> void finish(Request<T> request) {
        // Remove from the set of requests currently being processed.
        synchronized (mCurrentRequests) {
            mCurrentRequests.remove(request);
        }
        synchronized (mFinishedListeners) {
          for (RequestFinishedListener<T> listener : mFinishedListeners) {
            listener.onRequestFinished(request);
          }
        }

        if (request.shouldCache()) {
            synchronized (mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
                if (waitingRequests != null) {
                    mCacheQueue.addAll(waitingRequests);
                }
            }
        }
    }

      ....
}

RequestQueue是Volley中最核心的類,掌管著所有request的請求和調度的過程,可以說是一個中轉器的作用,我們從剛開始的start()方法看起:

/**
     * start()方法中啟動了緩存dispatcher跟網絡請求dispatcher,用來處理數據請求,
     * 如果緩存中有數據,則直接在緩存中獲取數據,緩存中沒有數據則從網絡中獲取數據。
     */
    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // 啟動緩存請求隊列
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

          //創造網絡請求隊列
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

在調用start()時候,主要做了兩件事情:

  • 調用stop()終止所有的dispatcher
  • 初始化所有的dispatcher,并且start(),dispatcher都是繼承于Thread的類

一般我們進行請求時候,都是使用的add(Request)方法來進行,所以接下來就查看一下這個方法的實現過程:

/**
     * 添加新的請求到請求對列當中
     */
    public <T> Request<T> add(Request<T> request) {
        // 設置對應關聯
        request.setRequestQueue(this);
          //添加到CurrentRequests中
        synchronized (mCurrentRequests) {
            mCurrentRequests.add(request);
        }

        // 按添加到請求隊列的順序請求數據
        request.setSequence(getSequenceNumber());
          //添加標志
        request.addMarker("add-to-queue");
          //當前request是否有緩存過,沒有直接加入到網絡請求隊列中
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }

        synchronized (mWaitingRequests) {
              //得到緩存key(該key為medthod+url的單一key)
            String cacheKey = request.getCacheKey();
              //是否包含該緩存key
            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;
        }
    }

在add()方法中主要的過程如圖所示:

reuqestqueue.add()

主要實現思路如下:

  1. 添加到mCurrentRequests中,mCurrentRequests當前請求以及未完成的請求的set,所有通過add()進來的都會加入到此set的當中。
  2. 通過request.shouldCache()來判斷是否需要緩存request,如果不需要則直接加入到網絡請求隊列當中,return;如果需要緩存當前request則判斷mWaitingRequests是否包含了該request的CacheKey的setKey,如果有,則加入相同的隊列當中,則put到mWaitingRequests當中,如果沒有包含了該request的CacheKey的setKey,置空與當前request的相同的queue(mWaitingRequests.put(cacheKey, null)),然后把request加入到緩存請求對列當中。

add()方法中主要是為了找到request的存放地,是網絡請求隊列還是緩存請求隊列,在判斷是否加入到緩存請求隊列時維護了一個mWaitingRequests集合來管理相同的請求,如果cacheKey相同,則不會加入到任何請對隊列當中,然后加入到mWaitingRequests存有相同SetKey的SetValue中,掛起。

Dispatcher

返回RequestQueue.start()中,這里會將CacheDispatcher和NetworkDispatchers啟動,兩種Dispatcher共同繼承于Thread來處理延時操作--網絡請求和緩存請求的過程,這里只詳細講述CacheDispatcher的run()過程,NetworkDispatchers只以簡述的形式講述。

CacheDispatcher在run方法中寫死一個無限循環,可以類比成handler機制中的Looper.loop()的設計思路,不斷的輪訓緩存隊列(mCacheQueue),從中取出request將其傳遞給mDelivery來進行處理。

@Override
    public void run() {
        //設置線程優先級
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // 初始化緩存相關參數
        mCache.initialize();

        while (true) {
            try {
                //從mCacheQueue取緩存
                final Request<?> request = mCacheQueue.take();
                  //添加緩存過的標記
                request.addMarker("cache-queue-take");

                // 當前request是否取消了
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

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

                // 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");
                //緩存數據是否需要更新
                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;

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

run方法內循環的過程中每次從mCacheQueue拿出一個request請求,對其添加已經訪問過的marker,如果在期間request被取消了,最終會調用RequestQueue.finish()方法從mCurrentRequests中remove掉,一次循環完成;如果request沒被取消則從緩存中獲取數據,并且判斷是否為空,如果為空則加入網絡隊列中重新請求,一次循環完成;如果不為空判斷是否過期,過期了則加入網絡隊列中重新請求,一次循環完成;如果不過期判斷是否需要更新緩存,需要則加入網絡隊列中重新請求,否則調用mDelivery.postResponse()完成數據傳遞的過程,執行圖如下:

NetworkDispatcher也繼承自Thread接口,run方法實現代碼如下:

@Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // Take a request from the queue.
                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");

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

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

                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");
                  //是否緩存結果
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // Post the response back.
                request.markDelivered();
                  //交付給mDelivery
                mDelivery.postResponse(request, response);
            } 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);
            }
        }
    }

在NetworkDispatcher中主要通過Network接口performRequest()方法獲得一個NetworkResponse,進而在轉換成Delivery.postResponse()可接受的Request對象進行傳遞,其中會判斷是否需要緩存該request以及相關error的捕獲并且傳遞給Delivery.postError()。

ResponseDelivery

無論是CacheDispatcher還是NetworkDispatcher,最終的結果都是交付由ResponseDelivery接口實現類來進行實現:

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

    /**
     * 處理從緩存和網絡上獲取的數據
     */
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable);

    /**
     *處理錯誤的情況
     */
    public void postError(Request<?> request, VolleyError error);
}

其具體實現類為ExecutorDelivery中,其代碼如下:

/**
 * 將請求結果傳遞給回調接口
 */
public class ExecutorDelivery implements ResponseDelivery {
    /** Used for posting responses, typically to the main thread. */
    private final Executor mResponsePoster;

    /**
     * 傳入Handler的原因,目的是為了與主線程進行交互
     * @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);
            }
        };
    }
    ...

    @Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
          //request傳遞標記
        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 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() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // 傳遞請求的結果,對應調用相應的回調接口,看mRequest對應實現類
              //如果是StringRequest,則回調對應接口。
            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();
            }
       }
    }
}

從Dispatcher那邊獲取到request和response最終傳遞到內部類ResponseDeliveryRunnable中進行處理,如果request是被finish的則丟棄傳遞,否則調用Request的對應的deliverResponse或者deliverError方法。這里要注意以下,該方法中傳遞了handler變量進來,這個變量是主線程的handler,也就保證了request的回調從工作線程切換回了主線程,其初始化代碼如下:

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

Request

Request類是一個抽象方法,其子類實現主要有:StringRequest,JsonRequest,ImageRequest .etc ,為了簡單查看后續步驟,我們拿StringRequest來查看:

/**
     * Delivers error message to the ErrorListener that the Request was
     * initialized with.
     *
     * @param error Error details
     */
    public void deliverError(VolleyError error) {
        if (mErrorListener != null) {
            mErrorListener.onErrorResponse(error);
        }
    }
  @Override
    protected void deliverResponse(String response) {
        if (mListener != null) {
            mListener.onResponse(response);
        }
    }

到這里就執行到了最終的回調,所有的過程也就完成了。

個人總結

個人認為volley的精髓在于面向接口編程,具有非常廣闊的拓展性,開發者完全可以自主寫一套自己的volley邏輯,充分解耦了各個模塊,使用組合的方式進行編程,是我們學習設計代碼的一個非常好的庫吧。

附上Volley過程圖:

volley流程圖

來自:http://www.jianshu.com/p/ff7b689cdbab

 

 本文由用戶 JamFelician 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!