編輯:關於Android編程
Volley現在已經被官方放到AOSP裡面,已經逐步成為Android官方推薦的網絡框架。
類抽象
對Http協議的抽象
Requeset
顧名思義,對請求的封裝,實現了Comparable接口,因為在Volley中是可以指定請求的優先級的,實現Comparable是為了在Request任務隊列中進行排序,優先級高的Request會被優先調度執行。
NetworkResponse
Http響應的封裝,其中包括返回的狀態碼 頭部 數據等。
Response
給調用者返回的結果封裝,它比NetworkResponse更加簡單,只包含三個東西:數據 異常 和 Cache數據。
Network
對HttpClient的抽象,接受一個Request,返回一個NetworkResponse
反序列化抽象
所謂反序列化,就是將網絡中傳輸的對象變成一個Java對象,Volley中是通過擴展Request類來實現不同的反序列化功能,如JsonRequest StringRequest,我們也可以通過自己擴展一些Request子類,來實現對請求流的各種定制。
請求工作流抽象
RequestQueue
用來管理各種請求隊列,其中包含有4個隊列
a) 所有請求集合,通過RequestQueue.add()添加的Request都會被添加進來,當請求結束之後刪除。
b) 所有等待Request,這是Volley做的一點優化,想象一下,我們同時發出了三個一模一樣的Request,此時底層其實不必真正走三個網絡請求,而只需要走一個請求即可。所以Request1被add之後會被調度執行,而Request2 和Request3被加進來時,如果Request1還未執行完畢,那麼Request2和 Request3只需要等著Request1的結果即可。
c) 緩存隊列,其中的Request需要執行查找緩存的工作
d) 網絡工作隊列 其中的Request需要被執行網絡請求的工作
NetworkDispatcher
執行網絡Request的線程,它會從網絡工作隊列中取出一個請求,並執行。Volley默認有四個線程作為執行網絡請求的線程。
CacheDispatcher
執行Cache查找的線程,它會從緩存隊列中取出一個請求,然後查找該請求的本地緩存。Volley只有一個線程執行Cache任務。
ResponseDelivery
請求數據分發器,可以發布Request執行的結果。
Cache
對Cache的封裝,主要定義了如何存儲,獲取緩存,存取依據Request中的getCacheKey()來描述。
提交請求
Volley通過RequestQueue.add(Request)來往任務隊列中增加請求:

一個Request被提交之後有幾個去處:
1.Set<Request<?>> mCurrentRequests對應所有請求隊列。所有調用add的Request必然都會添加到這裡面來。
2.PriorityBlockingQueue<Request<?>> mNetworkQueue 對應網絡隊列。如果一個Request不需要緩存,那麼add之後會被直接添加到網絡隊列中。
3.PriorityBlockingQueue<Request<?>> mCacheQueue對應緩存請求。如果一個Request需要緩存,並且當前的RequestQueue中並沒有一個Request的getCacheKey和當前Request相同(可以認為一個請求),那麼加入緩存隊列,讓緩存工作線程來處理。
4.Map<String, Queue<Request<?>>> mWaitingRequests對應等待隊列。如果RequestQueue中已經有一個相同請求在處理,這裡只需要將這個Request放到等待隊列中,等之前的Request結果回來之後,進行處理即可。
Volley提交任務到隊列中是不是很簡單?下面來說說優先級請求的事情吧,你可能已經注意到了,上面兩個存放需要執行任務的隊列都是PriorityBlockingQueue,前面說了Request現實了Comparable,看看這個方法:
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
//mSequence表示請求序列號,add時,會通過一個計數器來指定
return left == right ?
this.mSequence - other.mSequence :
right.ordinal() - left.ordinal();
}
所以,如果我們的工作線程(NetworkDispatcher,CacheDispatcher)取任務時,自然會從頭部開始取。
這裡的優先級,僅僅是保證一個請求比另外一個請求先處理,而並不能保證一個高優先級請求一定會比低優先級的請求先回來
緩存工作線程處理
@Override
public void run() {
//初始化Cache
mCache.initialize();
Request<?> request;
while (true) {
//阻塞 獲取一個Cache任務
request = mCacheQueue.take();
try {
//已經被取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
//如果拿cache未果,放入網絡請求隊列
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
}
//緩存超時,放入網絡請求隊列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
//根據Cache構造Response
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
//是否超過軟過期
if (!entry.refreshNeeded()) {
// 直接返回Cache
mDelivery.postResponse(request, response);
} else {
request.setCacheEntry(entry);
//設置中間結果
response.intermediate = true;
//發送中間結果
final Request<?> finalRequest = request;
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
//中間結果完事之後,講請求放入網絡隊列
mNetworkQueue.put(finalRequest);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (Exception e) {
}
}
}
這裡可以看到Volley確實對緩存封裝很到位,各種情況都考慮到了,其中比較重要的兩點:
取出來的Cache並不僅僅是數據,同時還包括這次請求的一些Header
硬過期 軟過期
我們可以看到Cache中有兩個字段來描述緩存過期: Cache.ttl vs Cache.softTtl。什麼區別呢?如果ttl過期,那麼這個緩存永遠不會被使用了;如果softTtl沒有過期,這個數據直接返回;如果softTtl過期,那麼這次請求將有兩次返回,第一次返回這個Cahce,第二次返回網絡請求的結果。想想,這個是不是滿足我們很多場景呢?先進入頁面展示緩存,然後再刷新頁面;如果這個緩存太久了,可以等待網絡數據回來之後再展示數據,是不是很贊?
NetworkDispatcher
執行網絡請求的工作線程,默認有4個線程,它不停地從網絡隊列中取任務執行。
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
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;
}
//通過Http棧實現客戶端發送網絡請求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果緩存軟過期,那麼會重新走網絡;如果server返回304,表示上次之後請求結果數據本地並沒有過期,所以可以直接用本地的,因為之前Volley已經發過一次Response了,所以這裡就不需要再發送Response結果了。
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
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");
}
//發送結果
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);
}
}
}
Request
Request中主要封裝了一個請求的各類Http協議信息,比如 URL,請求方法,請求的優先級,請求重試的策略,緩存策略等。
這裡說一下其中比較有意思的重發策略,如果一次請求發生超時異常,比如SocketTimeoutException ConnectTimeoutException ,我們可以為Request配置一個RetryPolicy,你可以指定重發這個Request的次數,以及每次失敗之後重新設置這個請求的超時時間(第一次失敗之後,你可以調整第二次請求的超時時間增加,以減少失敗的可能性)。
反序列化
Request最重要的功能就是提供了內容的反序列化,通過不同的子類來實現不同的序列化功能。比如,如果請求結果是一個Json的對象,我們可以使用JsonObjectRequest,如果是一個普通字符,使用StringRequest,同時,我們也可以很方便的定制自己的Request,通過復寫Response<T> parseNetworkResponse(NetworkResponse response);方法即可。
默認的JsonRequest使用org.json中的Json解析,我們使用Gson來進行解析能夠構造一個更加通用的處理json返回的Request:
public class JsonGRequest<T> extends Request<T> {
private static Gson gson = new Gson();
private Response.Listener<T> mListener;
public JsonGRequest(String url, Response.ErrorListener listener,Response.Listener responseListener) {
super(url, listener);
this.mListener = mListener;
}
public JsonGRequest(int method, String url, Response.ErrorListener listener) {
super(method, url, listener);
}
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
return Response.success(gson.fromJson(new InputStreamReader(new ByteArrayInputStream(response.data)),getType()), HttpHeaderParser.parseCacheHeaders(response))
}
@Override
protected void deliverResponse(T response) {
if(mListener != null) {
mListener.onResponse(response);
}
}
//獲取指定的泛型類型
protected Type getType() {
Type superclass;
for(superclass = this.getClass().getGenericSuperclass(); superclass instanceof Class && !superclass.equals(JsonGRequest.class); superclass = ((Class)superclass).getGenericSuperclass()) {
;
}
if(superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
} else {
ParameterizedType parameterized = (ParameterizedType)superclass;
return parameterized.getActualTypeArguments()[0];
}
}
}
ImageRequest
Volley專門為圖片請求提供了ImageRequest,主要是反序列化了一下數據流到BitMap,還可以制定圖片的大小,質量等參數。
ImageLoader是Volley提供的一個用來加載圖片的工具,它的內部還是使用ImageRequest來實現的,主要新加的功能是增加了內存緩存,你可以通過配置ImageCache來設置內存緩存。
Android 序列化的存儲和讀取總結及簡單使用
Android 序列化1.序列化的目的 (1).永久的保存對象數據(將對象
android中DatePicker和TimePicker的使用方法詳解
本文以實例講述了android中DatePicker和TimePicker的使用方法,具體步驟如下:下面是實現具體功能的代碼,其中main.xml代碼為:<?
Android基礎入門教程——8.1.3 Android中的13種Drawable小結 Part 3
本節引言: 本節我們來把剩下的四種Drawable也學完,他們分別是: LayerDrawable,TransitionDrawable,Leve
android launcher3桌面快捷方式分析
背景一般情況下,為了讓用戶更方便的打開應用,程序會在桌面上生成一些快捷方式。本來呢,如果是原生的桌面,其實是十分簡單,直接調用系統相關的API就行了。但是眾多的系統廠商以