這個章節,我先介紹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方法,具體代碼分析我放到下一章節。