Android 網絡通訊框架Volley的解析

在2013年Google I/O大會上,Android開發團隊公佈了一個新的網絡通訊框架:Volley。它適合處理通訊頻繁的網絡操做,但對於每一次通訊的數據量則有較爲苛刻的限制。本文將介紹該通訊框架的用法(包括使用現成和自定義的Request),並從源碼的角度探究其工做機制。android

目前,Android系統中用於實現HTTP通訊的方式主要有HttpURLConnection和HttpClient兩個類[1],而封裝了它們的框架主要有AsyncHttpClientUniversal-Image-Loader等。Volley庫將HTTP通訊進一步簡單化,它的用法以下:web

首先,因爲HTTP通訊勢必要訪問網絡,咱們須要在Android項目的Manifest.xml文件中添加訪問網絡的許可[2]:算法

<uses-permission android:name="android.permission.INTERNET" /> json

接下來,要先創建一個請求隊列對象(RequestQueue):api

RequestQueue mQueue = Volley.newRequestQueue(context);數組

這裏的RequestQueue能夠緩存全部的HTTP請求,而後按照必定的算法併發地發出這些請求(具體算法將在接下來的部分中有進一步的講解)。因爲RequestQueue可以併發處理請求,咱們只須要在每個進行網絡通訊的Activity中創建一個RequestQueue對象便可。緩存

Android系統的網絡通訊可以處理多種媒體形式[3],而RequestQueue所能處理的具體請求也可根據數據類型分爲StringRequest、JSONRequest、ImageRequest和自定義的Request等幾種。接下來,本文將逐一講解上述網絡請求類的用法:服務器

1:StringRequest網絡

StringRequest能夠向服務器端發出請求,並將數據以String的形式返回。假設咱們須要建立一個請求,向百度請求其首頁的網頁HTML源代碼,則代碼以下所示:併發

StringRequest stringRequest = new StringRequest("http://www.baidu.com",  
                        new Response.Listener<String>() {  
                            @Override  
                            public void onResponse(String response) {  
                                 contentText.setText(response); 
                            }  
                        }, new Response.ErrorListener() {  
                            @Override  
                            public void onErrorResponse(VolleyError error) {  
                                Log.e("TAG", error.getMessage(), error);  
                            }  
                        });  

 

在這段代碼中,咱們建立了一個新的StringRequest對象。該對象的構造方法有三個參數,分別是目標服務器地址、響應成功時的回調和響應失敗時的回調。其中目標服務器地址是百度首頁;響應成功時的回調方法將contentText的內容設爲該Request返回的文字內容;響應失敗時的回調方法打印具體的錯誤信息。

在建立完StringRequest對象後,咱們須要將其添加到先前建立的RequestQueue對象中,以發送該請求。這部分的代碼很簡單,只有一行:

mQueue.add(stringRequest); 

接下來,只需運行程序,便可在屏幕上觀察到服務器端返回的數據:

  

HTTP協議定義了多種請求類型[4],一般咱們關心的只有GET請求和POST請求。在以上的例子中,咱們發出的是一個GET請求。而若是要發出一個POST請求,咱們就須要調用另外的構造方法。在該方法中,咱們能夠指定請求類型爲POST,而且重寫Request(各類具體Request共同的父類)中的getParams()方法,在其中輸入提交給服務器端的參數。代碼以下所示:

StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener) {  
    @Override  
    protected Map<String, String> getParams() throws AuthFailureError {  
        Map<String, String> map = new HashMap<String, String>();  
        map.put("params1", "value1");  
        map.put("params2", "value2");  
        return map;  
    }  
};          

2.JSONRequest

JSON是一種輕量級的數據交換格式它基於JavaScript的一個子集採用徹底獨立於語言的文本格式。這些特性使JSON成爲理想的數據交換語言易於人閱讀和編寫,同時也易於機器解析和生成[5]而顧名思義,JsonRequest就是用於和服務器端交換JSON格式的數據的。JsonRequest有兩個子類(JsonObjectRequest和JsonArrayRequest),分別用來處理單個的JSON對象和JSON的數組。

JsonObjectRequest的用法與上述的StringRequest相似,建立一個對象,在其構造方法中指定服務器URL地址和響應成功和失敗時的回調,再把它加入請求隊列中便可。代碼以下:

       RequestQueue mRequestQueue = Volley.newRequestQueue(this);

        String url = "http://pipes.yahooapis.com/pipes/pipe.run?_id=giWz8Vc33BG6rQEQo_NLYQ&_render=json";

 

        JsonObjectRequest jr = new JsonObjectRequest(Request.Method.GET,url,null,new Response.Listener<JSONObject>() {

            @Override

            public void onResponse(JSONObject response) {

                try{

                    JSONObject valueobj = response.getJSONObject("value");

                    JSONArray jsonArray = valueobj.getJSONArray("items");

                    for(int i=0;i<jsonArray.length();i++){

                        JSONObject item = jsonArray.getJSONObject(i);

                        String description = item.getString("description");

                        adapter.add(description);

                    }

                }catch (JSONException e) {

                    throw new RuntimeException(e);

                }

 

            }

        },new Response.ErrorListener() {

            @Override

            public void onErrorResponse(VolleyError error) {

                error.printStackTrace();

            }

        });

        mRequestQueue.add(jr);       

在該例中,咱們使用了一個雅虎提供的查詢新聞的接口,從中獲取了JSON格式的當天的新聞。獲取新聞以後,咱們在成功響應的回調方法中打印了返回的JSON數據,並取出了其中的一部分放在adapter中,從而實現將新聞內容顯示在屏幕上的效果。

 

3.ImageRequest

ImageRequest可用於從服務器端獲取圖片。它做爲Request的一個子類,也具備和上述Request具體類型相相似的使用方法。在建立RequestQueue以後,便可調用其構造方法。

 mImageView= (ImageView) findViewById(R.id.webImage);

        newRequestQueue = Volley.newRequestQueue(ImageRequestActivity.this);

        ImageRequest p_w_picpathRequest = new ImageRequest("http://imgt6.bdstatic.com/it/u=2,887966933&fm=19&gp=0.jpg",

                new Response.Listener<Bitmap>()

                {

                    @Override

                    public void onResponse(Bitmap response)

                    {

                        mImageView.setImageBitmap(response);

                    }

                }, 0, 0, Bitmap.Config.RGB_565, null);

        newRequestQueue.add(p_w_picpathRequest);

能夠看到,ImageRequest的構造函數接收六個參數,這比StringRequest和JsonRequest稍多一些:

第一個參數就是圖片的URL地址,這和其它的Request中的URL參數做用相同;

第二個參數是圖片請求成功的回調,這裏咱們將服務器返回的Bitmap設置到mImageView中;

第三和第四個參數分別用於指定容許圖片最大的寬度和高度;

第五個參數用於指定圖片的顏色屬性;

第六個參數是圖片請求失敗的回調,這裏咱們什麼都不作。

最後咱們一樣須要將這個請求輸入到請求隊列中。具體代碼和其它類型的Request是相同的。運行上述代碼後,咱們便可看到咱們的app從網絡上取得了以下的圖片,並把它顯示在mImageView的位置:

 

4.自定義Request

除上述各種型之外,Volley還支持自定義類型的Request。接下來,本文將以軟件學院學生服務平臺中的代碼爲例,說明自定義Request的用法。

在服務平臺的「校園活動」頁面中,用戶能夠看到最新的校園活動信息。爲了從服務器端或獲取相應信息,咱們就須要使用自定義的Request。代碼以下所示:

CampusEventResponse activityResponse = new CampusEventResponse(getActivity(),

                    !isLoadMore) {

                @Override

                public void onResponse(JSONObject result) {

                    super.onResponse(result);

                    Lgr.i(result.toString());

                    Message msg = mHandler.obtainMessage();

                    try {

                        msg.what = result.getInt("status");

                        if (isLoadMore) {

                            isMoreData = result.getJSONArray("body").length() == 0 ? false : true;

                        }

                    } catch (JSONException e) {

                        e.printStackTrace();

                    }

 

                    mHandler.sendMessage(msg);

                }

 

                @Override

                public Object onErrorStatus(CSTStatusInfo statusInfo) {

                    return super.onErrorStatus(statusInfo);

                }

 

                @Override

                public void onErrorResponse(VolleyError error) {

                    super.onErrorResponse(error);

                }

            };

 

            EventRequest eventRequest = new EventRequest(CSTRequest.Method.GET,

                    mEventCategory.getSubUrl(), null,

                    activityResponse).setPage(mCurrentPage).setPageSize(DEFAULT_PAGE_SIZE);

            mEngine.requestJson(eventRequest);

其中,CampusEventResponse是Response的一個派生類。在服務器端返回數據後,其中的onResponse方法將會獲得執行。而本例中生成的CampusEventResponse對象,則被做爲一個參數,輸入EventRequest的構造方法中,起到監聽器的做用。

EventRequest就是一種自定義的Request,它的代碼以下所示:

public class EventRequest extends CSTJsonRequest {

 

    private String subUrl;

 

    private int page;

 

    private int pageSize;

 

    private String keywords;

 

    private boolean hasParams = false;

 

    public EventRequest(int method, String subUrl,

            Map<String, String> params,

            CSTResponse<JSONObject> response) {

        super(method, subUrl, params, response);

        this.subUrl = subUrl;

    }

 

    @Override

    public String getUrl() {

        if (hasParams) {

            StringBuilder sb = new StringBuilder();

            sb.append("?");

            try {

                if (page > 0) {

                    sb.append("page=").append(URLEncoder.encode("" + page, "UTF-8")).append("&");

                }

                if (pageSize > 0) {

                    sb.append("pageSize=").append(URLEncoder.encode("" + pageSize, "UTF-8"))

                            .append("&");

                }

                if (keywords != null) {

                    sb.append("keywords=").append(URLEncoder.encode(keywords, "UTF-8")).append("&");

                }

            } catch (UnsupportedEncodingException e) {

                e.printStackTrace();

            }

            sb.deleteCharAt(sb.length() - 1);

 

            return super.getUrl() + sb.toString();

        }

        return super.getUrl();

    }

 

    public EventRequest setPage(int page) {

        this.page = page;

        hasParams = true;

        return this;

    }

 

    public EventRequest setPageSize(int pageSize) {

        this.pageSize = pageSize;

        hasParams = true;

        return this;

    }

 

    public EventRequest setKeywords(String keywords) {

        this.keywords = keywords;

        hasParams = true;

        return this;

    }

}

咱們能夠看到,它繼承了CSTJsonRequest類(一樣是一個自定義Request類,接下來會有講解)。EventRequest除了構造方法以外,還重寫了Request類中的getURL方法。該方法能夠根據Page、PageSize和keywords三個變量構造出所需的URL。此外,該類中還有相應的方法可用於設置上述三個變量。

CSTJsonRequest是EventRequest的父類,它的代碼以下所示:

public class CSTJsonRequest extends CSTRequest<JSONObject> {

 

    private static final String BASE_URL = "http://www.cst.zju.edu.cn/isst";

 

    public CSTJsonRequest(int method, String subUrl, Map<String, String> params,

        CSTResponse<JSONObject> response) {

        super(method, BASE_URL + subUrl, params, response);

    }

 

    @Override

    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {

        try {

            String jsonString = new String(response.data,

                    HttpHeaderParser.parseCharset(response.headers));

            CSTHttpUtil.refreshCookies(BASE_URL, response.headers);

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

        }

    }

}

 

能夠看到,該類繼承了CSTRequest類,並在構造方法中調用了其父類的構造方法。

另外,它實現了parseNetworkResponse方法,用於解析從服務器端返回的JSON數據。若成功響應,則返回Response.success方法,並將解析出的包含JSON內容的String做爲參數輸入其中。若是解析不成功,則執行Response.error方法,並將拋出的異常對象做爲參數。

綜上所述,爲了從服務器端請求相應數據,須要創建一個Response派生類對象,並在其onResponse中指定成功返回時執行的內容。而後將該對象做爲監聽器,傳入自定義Request對象的構造方法中,最後將Request對象加入請求隊列。其中自定義Request的父類(一樣也是Request的派生類)中重寫了parseNetworkResponse方法,將服務器返回的數據轉化爲一個String。而後Response.success方法被調用,該String被從新轉化爲一個JSON對象並向上傳遞。

4.Volley框架源碼解析

 

Volley的官方文檔中附有一張Volley的工做流程圖,以下圖所示。

spacer.gif

從這張工做流程圖中咱們能夠看到:在一個請求被加入到緩存隊列中之後,它將被CacheDispatcher分配到一個合適的去向。若是在cache中已經存有該請求所對應的數據,那麼就經過cache線程從cache中讀取數據,將其解析後返回主線程;若是在cache中未能找到相應數據,則啓動network線程,將其經過NetworkDispatcher發送到網絡,從服務器端取回數據,寫到cache中,並將其解析後返回給主線程。接下來,本文將對Volley請求的源碼做一初步分析,並在此過程當中深刻說明上圖所示的工做機制。

在使用Volley時,如先前的例子所示,咱們老是須要先創建一個RequestQueue。在上文中,咱們使用的代碼是

Volley.newRequestQueue(context);

該方法的代碼以下所示:

public static RequestQueue newRequestQueue(Context context) {  

    return newRequestQueue(context, null);  

}  

 

能夠看到:該方法內只有一行代碼,它調用了newRequestQueue()並傳入了context和null做爲參數。而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 {  

            stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  

        }  

    }  

    Network network = new BasicNetwork(stack);  

    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  

    queue.start();  

    return queue;  

}  

 

能夠看到,該方法所建立的stack取決於用戶的系統版本:若是大於等於9,則這裏會在第12行建立一個新的HurlStack;反之,則會將一個HttpClientStack對象賦給stack。接下來,在用stack做爲參數建立一個Network對象(網絡線程實際上是調用 Network對象去實現跟網絡進行溝通的)以後,就能夠調用RequestQueue的構造方法了。在該方法中,cache文件和Network對象被做爲參數傳遞進去。最後,調用RequestQueue的start()方法,便可啓動該請求隊列。

其中,HurlStack和HttpClientStack是網絡訪問的最低層實現。因爲android的網絡訪問在2.2之前是用的阿帕奇的網絡訪問框架,2.2之後用的是HttpConnectUrl,volley兼容2.2如下的android版本,因此它須要針對版原本提供不一樣的實現。HurlStack對應的是HttpConnectUrl,而HttpClientStack對應的是阿帕奇的網絡訪問框架。

 

其中的Network是一個接口,這裏具體的實現是BasicNetwork,它的主要功能就是利用performRequest方法處理網絡通訊過程當中的具體細節。該方法的代碼以下所示:

public class BasicNetwork implements Network {  

    ……  

    @Override  

    public NetworkResponse performRequest(Request<?> request) throws VolleyError {  

        long requestStart = SystemClock.elapsedRealtime();  

        while (true) {  

            HttpResponse httpResponse = null;  

            byte[] responseContents = null;  

            Map<String, String> responseHeaders = new HashMap<String, String>();  

            try {  

                // Gather headers.  

                Map<String, String> headers = new HashMap<String, String>();  

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

                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,  

                            request.getCacheEntry() == null ? null : request.getCacheEntry().data,  

                            responseHeaders, true);  

                }  

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

            } catch (Exception e) {  

                ……  

            }  

        }  

    }  

}  

其中的第14行調用了performRequest()方法,這裏的HttpStack就是在一開始調用newRequestQueue()方法建立的實例,取決於系統版本,它既多是HurlStack也多是一個HttpClientStack。在獲得數據以後服務器返回的數據會被組裝成一個NetworkResponse對象並做爲該方法的返回值

 

RequestQueue的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;  

        networkDispatcher.start();  

    }  

}  

在本方法中,上述代碼建立了一個CacheDispatcher實例和若干個(默認狀況下爲四個)networkDispatcher實例,並調用了它們的start()方法。其中,CacheDispatcher和networkDispatcher都是Thread的子類,它們分別是用於處理緩存和網絡請求的線程。

接下來,咱們在構建本身的Request以後,就須要將它們添加到網絡請求隊列中了。RequestQueue的add()方法在本文中已經出現屢次。它的內容以下:

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

    // Tag the request as belonging to this queue and add it to the set of current requests.  

    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;  

    }  

}  

  add方法有如下幾個步驟:

1:判斷當前的Request是否使用緩存。如不使用,則將請求加入mNetworkQueue並直接返回;如使用,則判斷以前是否有執行相同的請求且尚未返回結果。

2:若是上一步最後的判斷是true,將此請求加入mWaitingRequests隊列,再也不重複請求,在上一個請求返回時直接發送結果。

3:若是第1步最後的判斷是false,將請求加入緩存隊列mCacheQueue,並將其CacheKey加入mWaitingRequests中。

可見,add方法並無執行任何實際的請求操做。實際的操做是由CacheDispatcher和NetworkDispatcher這兩個類完成的。其中CacheDispatcher是Thread的子類,具體代碼以下:

public class CacheDispatcher extends Thread {  

  

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

        while (true) {  

            try {  

                // Get a request from the cache triage queue, blocking until  

                // at least one is available.  

                final Request<?> request = mCacheQueue.take();  

                request.addMarker("cache-queue-take");  

                // If the request has been canceled, don't bother dispatching it.  

                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;  

            }  

        }  

    }  

}  

 

上述代碼主要完成如下工做:

1:從mCacheQueue取出請求。

2:檢查請求所對應的CacheKey在緩存中是否存在

3:若是不存在,就加到mNetworkQueue中,並繼續取下一個請求。若是存在,判斷是否過時。

4:若是過時,就加入網絡隊列mNetworkQueue中,並繼續取下一個請求。若是沒過時,判斷是否須要刷新。

5:若是不須要刷新,直接派發結果。若是須要刷新,調用mDelivery.postResponse派發結果,並將請求加入網絡隊列從新請求最新數據。

 

而用於處理網絡請求的NetworkDispatcher代碼以下所示:

public class NetworkDispatcher extends Thread {  

    ……  

    @Override  

    public void run() {  

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  

        Request<?> request;  

        while (true) {  

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

                // Perform the network 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) {  

                parseAndDeliverNetworkError(request, volleyError);  

            } catch (Exception e) {  

                VolleyLog.e(e, "Unhandled exception %s", e.toString());  

                mDelivery.postError(request, new VolleyError(e));  

            }  

        }  

    }  

}  

NetworkDispatcher的工做原理與CacheDispatcher類似。當start方法被調用後,它將不停地從mNetworkQueue取出請求,而後經過Network接口向網絡發送請求。

在取回請求結果後,若是服務器返回304,而且結果已經經過緩存派發了,那麼什麼也不作。不然調用Request的parseNetworkResponse方法解析請求結果,並派髮結果。

相關文章
相關標籤/搜索