Volley源碼解析(一)

這個章節,我先介紹volley功能介紹、總體設計、類設計、主要流程的源碼解析,其他方法,例如圖片加載、HurlStack、HttpClientStack、performRequest等,我會在後面進行介紹。

1、功能介紹
1.1 volley是異步網絡請求框架和圖片加載器。
1.2 優點
1、請求隊列和請求優先級。
2、請求Cache和內存管理。
3、擴展性強,大多是基於接口的設計,可配置型強。
4、可以取消請求。
5、提供簡單的圖片加載工具。

2、總體設計
2.1 總體設計圖如下:
這裏寫圖片描述
通過Dispatch Thread不斷從RequestQueue中獲取請求,根據是否已緩存調用Cache或者Network這兩類數據獲取接口其中之一,從內存緩存或者是服務器中獲取數據,然後交給ResponseDelivery去做結果分發以及回調處理。

2.2 核心功能點概念
volley使用,通過newRequestQueue()函數新建並啓動一個請求隊列RequestQueue,然後不斷向這個隊列中添加Request就可以了。
Volley:對外暴露的API,通過newRequestQueue()函數新建並啓動一個請求隊列RequestQueue。
Request:表示一個請求的抽象類。StringRequest、JsonRequest、ImageRequest都是它的子類,表示某種類型的請求。
RequestQueue:請求隊列,裏面包含一個CacheDispatcher(緩存請求的線程)、多個NetworkDispatcher(處理網絡請求的線程),start函數啓動CacheDispatcher和NetworkDispatcher。
CacheDispatcher:用於處理緩存請求。不斷從緩存請求隊列中獲取請求處理,隊列爲空的時候則等待,請求處理結束後將結果傳遞給ResponseDelivery去執行後續處理。當結果沒有被緩存過、緩存失效等情況下,這個請求都要重新進入NetworkDispatcher去調度處理。
NetworkDispatcher:用於處理網絡請求。不斷從網絡請求隊列中獲取請求處理,隊列爲空的時候則等待,請求結果傳遞給ResponseDelivery去執行後續處理,並判斷結果是否要進行緩存。
ResponseDelivery:返回結果分發接口。
HttpStack:處理Http請求,返回請求結果。目前Volley中有基於HttpURLConnection的HurlStack和基於Apache HttpClient的HttpClientStack。
Network:調用HttpStack處理請求,將結果轉化爲可以被ResponseDelivery處理的NetworkResponse。
Cache:緩存請求結果,默認使用sdcard的DiskBasedCache。NetworkDispatcher得到結果後判斷是否需要存在Cache,CacheDispatcher會從Cache中獲取緩存結果。
2.3 流程圖
這裏寫圖片描述

3、類設計圖
這裏寫圖片描述
紅色部分,圍繞RequestQueue類,將各個功能點以組合
形式結合在一起。每個功能點(Request,CacheDispatcher,NetworkdDispatcher),都是以接口或者抽象類的形式提供。紅色外面的部分,爲功能點提供具體實現。
多用組合,少用繼承;針對接口編程,不針對具體實現編程。

4、主要流程源碼解析
4.1.1
使用volley的時候,需要創建一個請求隊列RequestQueue,最好在AppApplication中聲明一個全局的RequestQueue,使用方法如下:

mRequestQueue = Volley.newRequestQueue(getApplicationContext());

我們看一下源碼中Volley中的newRequestQueue方法,就是新建一個RequestQueue類對象,如下:

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

這段代碼,將緩存對象和網絡對象加入到RequestQueue隊列中(其中DiskBasedCache是Cache的具體實現類,BasicNetWork是NetWork的具體實現類),RequestQueue中第二個參數network實現如下:

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

代碼中可以看出來,當API>=9的時候,使用HurlStack,反之,使用HttpClientStack。關於HurlStack和HttpClientStack基於什麼來實現的,我會在下一章中做詳細介紹。

4.1.2
添加request,使用方法如下:

mRequestQueue.add(request);

我們看一下源碼中RequestQueue中的add方法。

if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;}

首先判斷當前請求是否可以緩存,如果不能直接添加到網絡請求隊列,如果可以緩存,就加入緩存隊列。
然後判斷等待請求mWaitingRequests和添加的request是否有相同的cachekey,如果有的話,將這個request加入具有相同cachekey的等待隊列,如下:

stagedRequests.add(request);//  stagedRequests是具有cachekey的隊列
mWaitingRequests.put(cacheKey, stagedRequests);//mWaitingRequests是            
                                              //map<String,Queue>類型

如果沒有的話,就爲cachekey添加一個爲null的隊列,加入緩存隊列mCacheQueue,相當於在內存中重新添加這個request,代碼如下:

mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);

其中mCacheQueue和mNetworkQueue分別代表緩存請求隊列和網絡請求隊列,mWaitingRequests是一個等待請求的集合,如果一個請求可以被緩存,並且有相同的cachekey,就可以全部進入此等待隊列。

4.2 創建JSON請求
volley自帶JsonObjectRequest和JsonArrayRequest來處理JSON對象請求和JSON數據請求。創建JsonObjectRequest對象,寫好response回調接口,把這個請求放到請求隊列中就可以了,使用方法如下(JsonArrayRequest使用方法也類似):

JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET,url, null,
            new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
                Log.d(TAG, response.toString());
             }
        }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
               VolleyLog.d(TAG, "Error: " + error.getMessage());
             }
        });
mRequestQueue.add(jsonObjReq, tag_json_obj);

我們從源碼分析JsonObjectRequest.java文件,JsonObjectRequest繼承Response.java,就是一個結構體,裏面包含了url,請求方式,onResponse和errorResponse回調。

4.3 創建String請求
volley中有StringRequest類,用來請求string類型的數據,使用方法如下:

StringRequest strReq = new StringRequest(Method.GET,
            url, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Log.d(TAG, response.toString());
                    pDialog.hide();
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());
                    pDialog.hide();
                }
            });
mRequestQueue.add(strReq, tag_json_obj);

StringRequest.java跟JsonRequest結構一樣。

4.4 創建POST請求
上面都是GET請求,下面是POST請求,需要將請求類型改爲POST類型,並且override Request中的getParams方法就可以了,使用方法如下:

JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.POST,
            url, null,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    Log.d(TAG, response.toString());
                    pDialog.hide();
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());
                    pDialog.hide();
                }
            }) {

        @Override
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<String, String>();
            params.put("name", "Androidhive");
            params.put("email", "[email protected]");
            params.put("password", "password123");
            return params;
        }
    };

這裏通過重寫Request.java中的getParams方法,將POST中的參數傳入到request中,類型是Map

JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.POST,url, null,new Response.Listener<JSONObject>() {
    @Override
    public void onResponse(JSONObject response) {
        Log.d(TAG, response.toString());
        pDialog.hide();
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.d(TAG, "Error: " + error.getMessage());
        pDialog.hide();
    }
}) {

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    HashMap<String, String> headers = new HashMap<String, String>();
    headers.put("Content-Type", "application/json");
    headers.put("apiKey", "xxxxxxxxxxxxxxx");
    return headers;
}
};

這裏通過重寫Request.java中的getHeaders方法,將POST中的參數傳入到request中,類型是Map

Cache=mRequestQueue.getCache();
Entry entry = cache.get(url);
if(entry != null){
    try {
        String data = new String(entry.data, "UTF-8");
    } catch (UnsupportedEncodingException e) {      
        e.printStackTrace();
        }
    }
}else{
    // Cached response doesn't exists. Make network call here
}

2)請求緩存失效

mRequestQueue.getCache().invalidate(url, true);

3)關閉cache

StringRequest stringReq = new StringRequest(....);
stringReq.setShouldCache(false);//true就是打開cache

4)將URL的cache刪除

mRequestQueue.getCache().remove(url);

5)刪除所有cache

mRequestQueue.getCache().clear();

4.8 請求優先級
優先級分爲:Normal, Low, Immediate, High

private Priority priority = Priority.HIGH;
StringRequest strReq = new StringRequest(Method.GET,
            Const.URL_STRING_REQ, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Log.d(TAG, response.toString());
                    msgResponse.setText(response.toString());
                    hideProgressDialog();

                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());
                    hideProgressDialog();
                }
            }) {
        @Override
        public Priority getPriority() {
            return priority;
        }
    };

4.9 取消請求
addToRequestQueue(request, tag)方法還接受一個tag參數,這個tag就是用來標記某一類請求的,這樣就可以取消這個tag的所有請求了。

mRequestQueue.cancelAll("feed_request");

4.10 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是網絡分發器,有多個,然後我再去看一下CacheDispatcher分發器中的start方法,CacheDispatcher繼承的Thread,所以直接看run方法,代碼如下(註釋我寫在代碼上面):

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 {               
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                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 (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }
                //直接使用緩存中的數據
                request.addMarker("cache-hit");
                //parseNetworkResponse對數據進行解析
                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 {
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);
                    response.intermediate = true;

                    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) {                
                if (mQuit) {
                    return;
                }
                continue;
            }
        }

其中cache結構體中的內容,如上面介紹的cache機制的方法向cache中添加信息和內容。
同理,networkDispatcher中run方法源碼如下:

 public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                request = mQueue.take();
            } catch (InterruptedException e) {
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }
                addTrafficStatsTag(request);
                //發送網絡請求
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker(“network-http-complete");

                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

            // 在子線程中解析,在Request子類中parseNetworkResponse方法中實現解析
                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");
                }
                // response回調.
                request.markDelivered();
                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);
            }
        }
    }

代碼比較多,但是真正網絡請求的核心代碼應該是

NetworkResponse networkResponse = mNetwork.performRequest(request);

最核心的是performRequest方法,具體代碼分析我放到下一章節。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章