Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入了解Volley如何執行一個Request的流程

深入了解Volley如何執行一個Request的流程

編輯:關於Android編程

Volley主要類構成

Volley: 框架同名類,負責創建RequestQueue對象 Request:代表一個網絡請求的抽象類,具體實現子類有(StringRequest,JsonRequest,ImageRequest),需添加到RequestQueue中操作。包含了url,請求方法,請求Header,請求Body,請求的優先等級信息。 RequestQueue:核心類,負責維護所有的Request對象,完成請求操作。該類中有兩個基於優先級的Request隊列mCacheQueue,mNetWorkQueue(PriorityBlockingQueue),分別是緩存請求隊列,網絡請求隊列。該數據結構支持對添加進來的Request進行優先級排序(所以可以將理財列表內的請求設置成優先級最高),還有一個mCurrentRequest(SetRequest類型,用來維護一個已經加入到Request中,並且還沒有完成的請求;還有一個等待請求的集合,mWaitingQueue,如果一個請求正在處理並且可以被緩存,那麼後續的相同的url的請求,將被放入到等待隊列。 CacheDispatcher:緩存調度線程類,不停的從緩存隊列中取出Request請求,進行處理。 NetworkDispatcher:網絡調度線程類,不斷的從網絡請求隊列中取出Request去處理。 Cache:緩存接口,代表了一個可以獲取請求結果,存儲請求結果的緩存。 DiskBasedCache:基於disk的緩存實現類。 BasicNetwork:Volley中默認的網絡接口實現類 Response:封裝了經過解析後的數據,用於傳輸 ByteArrayPool:byte[] 的回收池,用於byte[]的再回收利用,減少了內存的分配和回收,是Volley提高性能的優化之一。 RetryPolicy:重試策略接口類 ResponseDelivery:請求結果的傳輸接口,用於傳遞請求結果或請求錯誤。 ExcutorDelivery:請求結果傳輸接口具體實現類。利用Handler將緩存調度線程或網絡調度線程中產生的請求結果和請求錯誤傳輸到主線程的回調函數中。

Volley一個請求的執行過程

volley官方文檔給出的執行一個請求的步驟是:

RequestQueue mQueue = Volley.newRequestQueue(context);  
mQueue.add(request);

為什麼創建一個mQueue以後,直接放入一個request,就可以執行該request了呢?發起網絡請求的代碼在哪裡呢?這裡看下newRequestQueue()做了哪些操作:

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        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();
            } 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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

可以看到創建RequestQueue後,調用了start()方法,再看下start()方法:

 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;
            VolleyCompat.getInstance().setNetworkDispatcherPriorityMax(networkDispatcher);
            networkDispatcher.start();
        }
    }

通過源碼可以查看到,CacheDispatcher,NetworkDispatcher都是繼承了Thread類,這裡相當於啟動了一個緩存調度線程,四個網絡調度線程,這裡先不分析緩存,看網絡調度線程做了些什麼操作:

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

                // 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);
            } catch (VolleyError volleyError) {

//                    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
//                    parseAndDeliverNetworkError(request, volleyError);

                //volley擴展:(2015-11-10)當出現錯誤,如設備無網絡,請求服務器出錯,並且有緩存時,需要將緩存內容展示出來
                VolleyCompat.getInstance().showCacheWhenErrorOccur(mCache,request,mDelivery,volleyError,startTimeMs);

            } catch (Exception e) {
                e.printStackTrace();
//                    VolleyLog.e(e, "Unhandled exception %s", e.toString());
//                    VolleyError volleyError = new VolleyError(e);
//                    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
//                    mDelivery.postError(request, volleyError);

                //volley擴展:(2015-11-10)當出現錯誤,如設備無網絡,請求服務器出錯,並且有緩存時,需要將緩存內容展示出來
                VolleyError volleyError = new VolleyError(e);
                VolleyCompat.getInstance().showCacheWhenErrorOccur(mCache,request,mDelivery,volleyError,startTimeMs);
            }
        }
    }

很簡單,網絡調度線程先是從隊列mQueue裡取出一個request,然後判斷該request是否已經取消,如果沒有被用戶取消,則調用mNetwork的performRequest(request)發起網絡請求,這裡的mNetWork是一個BasicNetWrok,看下performRequest()的實現,

@Override
    public NetworkResponse performRequest(Request request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map headers = new HashMap();
                addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                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);
                    }

                    // A HTTP 304 response does not have all header fields. We
                    // have to use the header fields from the cache entry plus
                    // the new ones from the response.
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // 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);
            } 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 {
                    throw new NoConnectionError(e);
                }
                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 {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }

首先通過調用mHttpStack.performRequest(request,headers) 發起請求,返回的首先是一個org.apache.http.HttpResponse對象,根據statusCode判斷,如果是SC_NOT_MODIFIED,並且支持緩存,那麼直接加載本地緩存,否則根據httpResponse.getEntity()方法來構建一個NetworkResponse的對象。

現在回到NetWorkDispatcher的run()方法裡,當得到一個NetWorkResponse對象後,接著調用request.parseNetWorkResponse(networkResponse)來對結果數據進行解析,解析數據的過程是什麼樣的呢?查看下request的parseNetWorkResponse()方法。

 abstract protected Response parseNetworkResponse(NetworkResponse response);

是給抽象方法,再看Request,也是一個抽象類,說明要想發起請求,必須創建一個Request的子類,並且自己實現該方法,由該Requet來決定如何解析這個networkResponse。好,那先看下Volley自己提供的StringRequest中該方法是怎麼實現的:

@Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }

對比下另外一個Volley提供的JsonObjectRequest的parseNetWorkResponse實現:

@Override
    protected Response parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
            return Response.success(new JSONObject(jsonString),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException je) {
            return Response.error(new ParseError(je));
        }
    }

其中Response.success()會new 出一個Response對象,該對象中的result變量也是泛型,並且該result的類型也是由新建Request類的子類的時候制定的,這樣這個請求就得到了一個Response對象。

接著回到NetWorkDispatcher的run()方法,接著分析下邊流程:

mDelivery.postResponse(request, response);

這個mDelivery又是什麼東西呢?查看NetWorkDispatcher源碼,其實是一個ResponseDelivery類型變量,而查看ResponseDelivery發現是一個接口,從這裡可以看到,Volley中處處用到了面向接口的編程思想,使得使用者可以自己定義子類型,提供相關服務。那麼這個mDelivery是在哪裡制定的呢?其實是在RequestQueue的構造方法中指定,然後在start()方法中傳遞給NetWorkDispatcher的:

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

好,接著看ExecutorDelivery的postResponse()方法:

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

調用了mResponsePoster的execute()方法,接著看下mResponsePoster:

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

可以看到看到handler,想必熟悉handler機制的童鞋們就特別想知道這個handler是哪個線程的handler了吧?會不會是UI Thread的呢?接著看

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

可以看到,確實是UI 線程的,這樣我們就可以將ResponseDeliveryRunnable放到UI線程裡去執行啦。好了,那看下讓UI線程執行什麼操作呢?

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

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

其中這個mRequest,就是我們這次網絡請求發送的Request,Response就是我們這次請求返回的結果封裝類,再次提醒,該Response裡的result變量的類型,就是我們在新建Request子類時候指定的類型。好的,萬裡長城差最後一步了,再看下Request的deliverResponse方法()

/**
     * Subclasses must implement this to perform delivery of the parsed
     * response to their listeners.  The given response is guaranteed to
     * be non-null; responses that fail to parse are not delivered.
     * @param response The parsed response returned by
     * {@link #parseNetworkResponse(NetworkResponse)}
     */
    abstract protected void deliverResponse(T response);

也是一個抽象方法,那麼我們還是看下JsonRequest類的該方法是如何實現的:

@Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

mListener是Response的一個內部接口:

public class Response {

    /** Callback interface for delivering parsed responses. */
    public interface Listener {
        /** Called when a response is received. */
        public void onResponse(T response);
    }

那mListener是在哪裡賦值的呢?

public JsonRequest(int method, String url, String requestBody, Listener listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
        mRequestBody = requestBody;
    }

也就是在創建JsonRequest的時候,就要指定這個Response.Listener,這裡也是面相接口編程,因為不知道將來誰會對這個Request請求的結果數據感興趣,那麼就在設計框架的時候,調用listener的onResponse()方法,這樣以後誰對結果數據敢興趣,那就實現接口,並且在創建相應Request的時候,傳遞進來就可以了。

有一個細節值得注意的就是,在Volley原生Request類的構造函數裡,是只有Response.ErrorListener,並沒有Response.Listener的:

/**
     * Creates a new request with the given method (one of the values from {@link Method}),
     * URL, and error listener.  Note that the normal response listener is not provided here as
     * delivery of responses is provided by subclasses, who have a better idea of how to deliver
     * an already-parsed response.
     */
    public Request(int method, String url, Response.ErrorListener listener) {
        mMethod = method;
        mUrl = url;
        mErrorListener = listener;
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
    }

也就是說請求如果發生錯誤信息了,那麼必須要有能通知到的listener來處理,但是請求成功後,可以任何事情也不做。

正查數據請求過來的流程分析清楚後,最後再看下請求出錯後的處理邏輯:

在ExecutorDelivery的run()方法裡:

// Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

而在Request的deliverError()方法中:

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

也印證了上面的請求出錯必須有處理的猜測。

至此,Volley一個請求的執行流程就分析完成了,總結下:

Volley在主線程中創建一個request請求後,就會將該請求放到隊列中,然後後台線程(NetWorkDispatcher)從隊列中取出請求,調用網絡相關類,執行請求,得到返回結果後,通過ExecutorDelivery將一個Runnable對象放到UI線程的消息池裡去執行。 在創建Request的時候,需要制定Response.Listener,Response.ErroListener對象,因為在Request執行完後,Volley會通過handler機制,將在UI線程裡調用這些listener的相關方法,從而使應用可以在這些listener的相關方法裡,獲得這些請求結果數據,從而實現刷新UI等操作。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved