博文出處:Volley框架源碼解析,歡迎大家關注我的博客膘侮,謝謝!
0001B
在 2013 年的 Google I/O 大會上的榛,Volley 網(wǎng)絡通信框架正式發(fā)布琼了。Volley 框架被設計為適用于網(wǎng)絡請求非常頻繁但是數(shù)據(jù)量并不是特別大的情景,正如它的名字一樣。Volley 相比其他網(wǎng)絡框架而言雕薪,采用了在 Android 2.3 以下使用 HttpClient 昧诱,而 Android 2.3 及以上使用 HttpUrlConnection 的方案。這是因為在 Android 2.3 以下時所袁,HttpUrlConnection 并不完善盏档,有很多 bug 存在。因此在 Android 2.3 以下最好使用 HttpClient 來進行網(wǎng)絡通信燥爷;而在 Android 2.3 及以上蜈亩,HttpUrlConnection 比起 HttpClient 來說更加簡單易用,修復了之前的 bug 前翎。所以在 Android 2.3 及以上我們使用 HttpUrlConnection 來進行網(wǎng)絡通信勺拣。
除此之外,Volley 框架還具有優(yōu)先級處理鱼填、可擴展性強等特點药有。雖然現(xiàn)在有 Retrofit 、OkHttp 等十分優(yōu)秀的網(wǎng)絡通信框架苹丸,但是深入理解 Volley 框架內部的思想可以大大提高我們自身的技術水平愤惰,畢竟僅僅停留在只會使用的階段是不行的哦。那么赘理,下面就進入我們今天的正題吧;卵浴( ps :本文篇幅過長,可能會引起不適商模,請在家長的陪同下觀看)
0010B
Volley 使用方法
在長篇大論地解析 Volley 框架源碼之前奠旺,我們先來看看平時是怎樣使用 Volley 的。(大攀┝鳎可直接跳過 -_- )
RequestQueue mQueue = Volley.newRequestQueue(context);
JsonObjectRequest request = new JsonObjectRequest(url, null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject jsonObject) {
// TODO
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
// TODO
}
});
mQueue.add(request);
我們通過 Volley.newRequestQueue(context)
來得到一個請求隊列的對象 mQueue
响疚,在隊列中暫存了我們所有 add 進去的 request ,之后一個個取出 request 來進行網(wǎng)絡通信瞪醋。一般來說忿晕,在一個應用程序中,只保持一個請求隊列的對象银受。
之后創(chuàng)建了 JsonObjectRequest 對象用來請求 JSON 數(shù)據(jù)践盼,并把它加入 mQueue
的隊列中。Volley 框架的使用方法非常簡單宾巍,并且有多種 request 請求方式可以選擇咕幻,使用方法都是和上面類似的。
0011B
在這先把 Volley 框架中幾個重要的類的作用講一下顶霞,以便看源碼時能夠更加明白:
- RequestQueue :這個大家一看都明白肄程,用來緩存 request 的請求隊列,根據(jù)優(yōu)先級高低排列;
- Request :表示網(wǎng)絡請求绷耍,本身是一個抽象類吐限,子類有 StringRequest 、JsonRequest 褂始、ImageRequest 等诸典;
- Response :表示網(wǎng)絡請求后的響應,也是一個抽象類崎苗。內部定義了 Listener 狐粱、ErrorListener 接口;
- NetworkResponse :對返回的 HttpResponse 內容進行了封裝胆数,雖然類名和 Response 差不多肌蜻,但是不是 Response 的子類;
- CacheDispatcher :一個處理請求緩存的線程必尼。不斷從 RequestQueue 中取出 Request 蒋搜,然后取得該 Request 對應的緩存,若緩存存在就調用 ResponseDelivery 做后續(xù)分發(fā)處理判莉;如果沒有緩存或者緩存失效需要進入 NetworkDispatcher 中從網(wǎng)絡上獲取結果豆挽;
- NetworkDispatcher :一個處理網(wǎng)絡請求的線程。和 CacheDispatcher 類似券盅,從網(wǎng)絡上得到響應后調用 ResponseDelivery 做后續(xù)分發(fā)處理帮哈。而且根據(jù)需求判斷是否需要做緩存處理;
- ResponseDelivery :用作分發(fā)處理锰镀。利用 Handler 把結果回調到主線程中娘侍,即 Listener 、ErrorListener 接口泳炉。主要實現(xiàn)類為 ExecutorDelivery 憾筏;
- HttpStack :主要作用就是發(fā)起 Http 請求。子類分為 HurlStack 和 HttpClientStack 胡桃,分別對應著 HttpUrlConnection 和 HttpClient 踩叭;
- Network :處理 Stack 發(fā)起的 Http 請求,把 Request 轉換為 Response 翠胰,主要實現(xiàn)類為 BasicNetwork ;
- RetryPolicy :請求重試策略自脯。主要實現(xiàn)類為 DefaultRetryPolicy 之景;
- Cache :網(wǎng)絡請求的緩存。在 CacheDispatcher 中獲取 Cache 膏潮,在 NetworkDispatcher 中判斷是否保存 Cache 锻狗。主要實現(xiàn)類為 DiskBasedCache ,緩存在磁盤中。
Volley
看完了之后轻纪,我們就要開始源碼解析油额。我們入手點就是 Volley.newRequestQueue(context)
了。
public class Volley {
/** 默認的磁盤緩存目錄名 */
private static final String DEFAULT_CACHE_DIR = "volley";
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
// 設置 UA
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
// 根據(jù) Android SDK 版本設置 HttpStack 刻帚,分為 HurlStack 和 HttpClientStack
// 分別對應著 HttpUrlConnection 和 HttpClient
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 network = new BasicNetwork(stack);
// 創(chuàng)建 RequestQueue
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}
從上面 Volley 類的源碼中可知潦嘶,Volley 類主要就是用來創(chuàng)建 RequestQueue 的。我們之前使用的 newRequestQueue(Context context)
方法最終會調用 newRequestQueue(Context context, HttpStack stack)
崇众。Volley 允許我們使用自定義的 HttpStack 掂僵,從這也可以看出 Volley 具有很強的擴展性。
RequestQueue
接下來繼續(xù)跟蹤 RequestQueue 構造方法的代碼顷歌。
// 默認線程池數(shù)量為 4
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
在構造方法中創(chuàng)建了 ExecutorDelivery 對象锰蓬,ExecutorDelivery 中傳入的 Handler 為主線程的,方便得到 Response 后回調眯漩;NetworkDispatcher[] 數(shù)組對象芹扭,默認數(shù)組的長度為 4 ,也就意味著默認處理請求的線程最多為 4 個赦抖。
在 Volley.newRequestQueue(Context context, HttpStack stack)
中創(chuàng)建完 RequestQueue 對象 queue
之后舱卡,還調用了 queue.start()
方法。主要用于啟動 queue
中的 mCacheDispatcher
和 mDispatchers
摹芙。
/** 請求緩存隊列 */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
/** 網(wǎng)絡請求隊列 */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
public void start() {
stop(); // 確保當前 RequestQueue 中的 mCacheDispatcher 和 mDispatchers[] 是關閉的
// 創(chuàng)建 mCacheDispatcher 灼狰,并且開啟
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// 根據(jù) mDispatchers[] 數(shù)組的長度創(chuàng)建 networkDispatcher ,并且開啟
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
// 關閉當前的 mCacheDispatcher 和 mDispatchers[]
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
void finish(Request<?> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
// 從 mCurrentRequests 中移除該 request
mCurrentRequests.remove(request);
}
// 如果 request 是可以被緩存的浮禾,那么從 mWaitingRequests 中移除交胚,加入到 mCacheQueue 中
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
}
}
}
}
那么看到這里我們意識到有必要看一下 CacheDispatcher 和 NetworkDispatcher 的代碼。我們先暫且放一下盈电,來看看 RequestQueue 的 add
方法蝴簇。add
方法就是把 Request 加入到 RequestQueue 中了:
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
// 當前正在請求的 Set 集合
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
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);
}
// 設置序列號,該序列號為 AtomInteger 自增的值
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 如果該 request 不該緩存匆帚,則直接加入 mNetworkQueue 熬词,跳過下面的步驟
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
synchronized (mWaitingRequests) {
// 其實 cacheKey 就是 request 的 url
String cacheKey = request.getCacheKey();
// 如果該 mWaitingRequests 已經(jīng)包含了有該 cacheKey
if (mWaitingRequests.containsKey(cacheKey)) {
// 得到該 cacheKey 對應的 Queue
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
// 把該 request 加入到 mWaitingRequests
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// 如果沒有,那么將該 request 加入到 mCacheQueue 中
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
在 add(Request<T> request)
方法中吸重,額外使用了兩個集合來維護 Request 互拾,其中
- mCurrentRequests :用來維護正在做請求操作的 Request;
- mWaitingRequests :主要作用是如果當前有一個 Request 正在請求并且是可以緩存的嚎幸,那么 Volley 會去 mWaitingRequests 中根據(jù)該 cacheKey 查詢之前有沒有一樣的 Request 被加入到 mWaitingRequests 中颜矿。若有,那么該 Request 就不需要再被緩存了嫉晶;若沒有就加入到 mCacheQueue 中進行后續(xù)操作骑疆。
現(xiàn)在我們來看看 CacheDispatcher 和 NetworkDispatcher 類的源碼田篇。
CacheDispatcher
首先是 CacheDispatcher 的:
public class CacheDispatcher extends Thread {
...... // 省略部分源碼
// 結束當前線程
public void quit() {
mQuit = true;
interrupt();
}
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化 mCache ,讀取磁盤中的緩存文件箍铭,加載到 mCache 中的 map 中
// 會造成線程阻塞泊柬,要在子線程中調用
mCache.initialize();
while (true) {
try {
// 從緩存隊列中取出 request ,若沒有則會阻塞
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果該 request 被標記為取消诈火,則跳過該 request 兽赁,不分發(fā)
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 根據(jù) request 的 url 去獲得緩存
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// 沒有緩存,把 Request 放入網(wǎng)絡請求隊列中
mNetworkQueue.put(request);
continue;
}
// 若緩存失效柄瑰,也放入網(wǎng)絡請求隊列中
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// 緩存存在闸氮,把緩存轉換為 Response
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 判斷緩存是否需要刷新
if (!entry.refreshNeeded()) {
// 不需要刷新就直接讓 mDelivery 分發(fā)
mDelivery.postResponse(request, response);
} else {
// 需要刷新緩存
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// 先設置一個標志,表明該緩存可以先分發(fā)教沾,之后需要重新刷新
response.intermediate = true;
// 利用 mDelivery 先把 response 分發(fā)下去蒲跨,之后還要把該 request 加入到 mNetworkQueue 重新請求一遍
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;
}
}
}
}
CacheDispatcher 類主要的代碼就如上面所示了,在主要的 run()
方法中都添加了注釋授翻,閱讀起來應該比較簡單或悲。那么在這里就貢獻一張 CacheDispatcher 類的流程圖:
NetworkDispatcher
然后是 NetworkDispatcher 的代碼:
public class NetworkDispatcher extends Thread {
...... // 省略部分源碼
public void quit() {
mQuit = true;
interrupt();
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void addTrafficStatsTag(Request<?> request) {
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
}
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
try {
// 取出 request
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");
// 判斷是否被取消,和 CacheDispatcher 中的步驟一致
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
// 設置線程標識
addTrafficStatsTag(request);
// 處理網(wǎng)絡請求
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服務端返回 304 并且已經(jīng)分發(fā)了一個響應堪唐,那么不再進行二次分發(fā)
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 得到 response
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果該 request 需要進行緩存巡语,那么保存緩存
// 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");
}
// 標記該 request 對應的 response 正在分發(fā)中
request.markDelivered();
// 分發(fā)該 request 對應的 response
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
// 分發(fā)該 request 對應的 error
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
// 分發(fā)該 request 對應的 error
mDelivery.postError(request, new VolleyError(e));
}
}
}
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
}
同樣的,根據(jù) NetworkDispatcher 我們也可以梳理出一張流程圖:
Request
到這里淮菠,我們把目光轉向 Request 男公。Request 是一個抽象類:
public abstract class Request<T> implements Comparable<Request<T>> {
...// 省略絕大部分源碼-_- !
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
abstract protected void deliverResponse(T response);
protected VolleyError parseNetworkError(VolleyError volleyError) {
return volleyError;
}
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
// request 完成響應分發(fā)后調用
void finish(final String tag) {
if (mRequestQueue != null) {
// 跳轉到 RequestQueue.finish 方法
mRequestQueue.finish(this);
}
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
// If we finish marking off of the main thread, we need to
// actually do it on the main thread to ensure correct ordering.
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
} else {
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("%d ms: %s", requestTime, this.toString());
}
}
}
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
// request 優(yōu)先級高的比低的排在隊列前面,優(yōu)先被請求
// 如果優(yōu)先級一樣合陵,按照 FIFO 的原則排列
return left == right ?
this.mSequence - other.mSequence :
right.ordinal() - left.ordinal();
}
}
Request 實現(xiàn)了 Comparable 接口枢赔,這是因為 Request 是有優(yōu)先級的,優(yōu)先級高比優(yōu)先級低的要先響應拥知,排列在前踏拜。默認有四種優(yōu)先級:
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
另外,子類繼承 Request 還要實現(xiàn)兩個抽象方法:
- parseNetworkResponse :把 NetworkResponse 轉換為合適類型的 Response低剔;
- deliverResponse :把解析出來的類型分發(fā)給監(jiān)聽器回調速梗。
另外,Request 還支持八種請求方式:
/**
* Supported request methods.
*/
public interface Method {
int DEPRECATED_GET_OR_POST = -1;
int GET = 0;
int POST = 1;
int PUT = 2;
int DELETE = 3;
int HEAD = 4;
int OPTIONS = 5;
int TRACE = 6;
int PATCH = 7;
}
在 Volley 中襟齿,Request 的子類眾多姻锁,有 StringRequest 、JsonObjectRequest(繼承自 JsonRequest ) 猜欺、JsonArrayRequest(繼承自 JsonRequest ) 和 ImageRequest 等屋摔。當然這些子類并不能滿足全部的場景要求,而這就需要我們開發(fā)者自己動手去擴展了替梨。
下面我就分析一下 StringRequest 的源碼钓试,其他子類的源碼都是類似的,可以回去自行研究副瀑。
public class StringRequest extends Request<String> {
private final Listener<String> mListener;
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
protected void deliverResponse(String response) {
// 得到相應的 response 后弓熏,回調 Listener 的接口
mListener.onResponse(response);
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
// 把字節(jié)數(shù)組轉化為字符串
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
我們發(fā)現(xiàn) StringRequest 的源碼十分簡潔。在 parseNetworkResponse
方法中主要把 response 中的 data 轉化為對應的 String 類型糠睡。然后回調 Response.success
即可挽鞠。
看完了 Request 之后,我們來分析一下 Network 狈孔。
Network
Network 是一個接口信认,里面就一個方法 performRequest(Request<?> request)
:
public interface Network {
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
光看這個方法的定義就知道這個方法是用來干什么了!就是根據(jù)傳入的 Request 執(zhí)行均抽,轉換為對應的 NetworkResponse 的嫁赏,并且該 NetworkResponse 不為空。我們就跳到它的實現(xiàn)類中看看該方法具體是怎么樣的油挥。
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static int DEFAULT_POOL_SIZE = 4096;
protected final HttpStack mHttpStack;
protected final ByteArrayPool mPool;
public BasicNetwork(HttpStack httpStack) {
// 使用 ByteArrayPool 可以實現(xiàn)復用潦蝇,節(jié)約內存
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mPool = pool;
}
@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 {
// 得到請求頭
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());
// 如果返回的狀態(tài)碼是304(即:HttpStatus.SC_NOT_MODIFIED)
// 那么說明服務端的數(shù)據(jù)沒有變化,就直接從之前的緩存中取
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) {
// 把 entiity 轉為 byte[]
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// 如果該請求的響應時間超過 SLOW_REQUEST_THRESHOLD_MS(即 3000ms) ,會打印相應的日志
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
// 響應碼不是在 200-299 之間就拋異常
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} 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);
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);
}
}
}
}
/**
* 如果請求的響應時間超過 SLOW_REQUEST_THRESHOLD_MS 惋鹅,就打印相應的日志
*/
private void logSlowRequests(long requestLifetime, Request<?> request,
byte[] responseContents, StatusLine statusLine) {
if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
"[rc=%d], [retryCount=%s]", request, requestLifetime,
responseContents != null ? responseContents.length : "null",
statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
}
}
/**
* 進行重試策略则酝,如果不滿足重試的條件會拋出異常
*/
private static void attemptRetryOnException(String logPrefix, Request<?> request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
// 在請求行中添加緩存相關
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// If there's no cache entry, we're done.
if (entry == null) {
return;
}
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.serverDate > 0) {
Date refTime = new Date(entry.serverDate);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
protected void logError(String what, String url, long start) {
long now = SystemClock.elapsedRealtime();
VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
}
/**把 HttpEntity 的內容讀取到 byte[] 中. */
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
PoolingByteArrayOutputStream bytes =
new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
byte[] buffer = null;
try {
InputStream in = entity.getContent();
if (in == null) {
throw new ServerError();
}
buffer = mPool.getBuf(1024);
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
return bytes.toByteArray();
} finally {
try {
// Close the InputStream and release the resources by "consuming the content".
entity.consumeContent();
} catch (IOException e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
VolleyLog.v("Error occured when calling consumingContent");
}
mPool.returnBuf(buffer);
bytes.close();
}
}
/**
* 把 Headers[] 轉換為 Map<String, String>.
*/
private static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new HashMap<String, String>();
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}
}
我們把 BasicNetwork 的源碼全部看下來,發(fā)現(xiàn) BasicNetwork 干的事情就如下:
- 利用 HttpStack 執(zhí)行請求闰集,把響應 HttpResponse 封裝為 NetworkResponse ;
- 如果在這過程中出錯沽讹,會有重試策略。
至于 NetworkResponse 的源碼在這里就不分析了返十,主要是一個相對于 HttpResponse 的封裝類遭垛,可以自己去看其源碼涣雕。
得到 NetworkResponse 之后,在 NetworkDispatcher 中經(jīng)過 Request 的 parseNetworkResponse
方法把 NetworkResponse 轉化為了 Response 。(具體可參考上面分析的 NetworkDispatcher 和 StringRequest 源碼)
那么接下來就把目光轉向 Response 吧疆拘。
Response
Response 類的源碼比較簡單,一起來看看:
public class Response<T> {
/** 分發(fā)響應結果的接口 */
public interface Listener<T> {
/** Called when a response is received. */
public void onResponse(T response);
}
/** 分發(fā)響應錯誤的接口 */
public interface ErrorListener {
/**
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
public void onErrorResponse(VolleyError error);
}
/** 通過這個靜態(tài)方法構造 Response */
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
return new Response<T>(result, cacheEntry);
}
/**
* 通過這個靜態(tài)方法構造錯誤的 Response
*/
public static <T> Response<T> error(VolleyError error) {
return new Response<T>(error);
}
/** Parsed response, or null in the case of error. */
public final T result;
/** Cache metadata for this response, or null in the case of error. */
public final Cache.Entry cacheEntry;
/** Detailed error information if <code>errorCode != OK</code>. */
public final VolleyError error;
/** True if this response was a soft-expired one and a second one MAY be coming. */
public boolean intermediate = false;
/**
* 是否成功
*/
public boolean isSuccess() {
return error == null;
}
private Response(T result, Cache.Entry cacheEntry) {
this.result = result;
this.cacheEntry = cacheEntry;
this.error = null;
}
private Response(VolleyError error) {
this.result = null;
this.cacheEntry = null;
this.error = error;
}
}
Response 類主要通過 success
和 error
兩個方法分別來構造正確的響應結果和錯誤的響應結果狭园。另外缀蹄,在 Response 類中還有 Listener 和 ErrorListener 兩個接口。在最終的回調中會使用到它們排拷。
在得到了 Response 之后侧漓,就要使用 ResponseDelivery 來分發(fā)了。那下面就輪到 ResponseDelivery 了监氢,go on !!
ResponseDelivery
public interface ResponseDelivery {
/**
* 分發(fā)該 request 的 response
*/
public void postResponse(Request<?> request, Response<?> response);
/**
* 分發(fā)該 request 的response 布蔗,runnable 會在分發(fā)之后執(zhí)行
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* 分發(fā)該 request 錯誤的 response
*/
public void postError(Request<?> request, VolleyError error);
}
ResponseDelivery 的接口就定義了三個方法藤违,我們需要在其實現(xiàn)類中看看具體的實現(xiàn):
public class ExecutorDelivery implements ResponseDelivery {
/** 用來分發(fā) Response , 一般都是在主線程中*/
private final Executor mResponsePoster;
/**
* 傳入的 Handler 為主線程的
*/
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);
}
};
}
/**
* Creates a new response delivery interface, mockable version
* for testing.
* @param executor For running delivery tasks
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
/**
* A Runnable 用來分發(fā) response 到主線程的回調接口中
*/
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// 如果 request 被標記為取消,那么不用分發(fā)
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 根據(jù) mResponse 是否成功來分發(fā)到不同的方法
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");
}
// 運行 mRunnable
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
ResponseDelivery 將根據(jù) mResponse 是否成功來調用不同的方法 mRequest.deliverResponse
和 mRequest.deliverError
纵揍。在 mRequest.deliverResponse
中會回調 Listener 的 onResponse
方法顿乒;而在 mRequest.deliverError
中會回調 ErrorListener 的 onErrorResponse
方法。至此泽谨,一個完整的網(wǎng)絡請求及響應流程走完了璧榄。
HttpStack
現(xiàn)在回過頭來看看 Volley 框架中是如何發(fā)起網(wǎng)絡請求的。在本文的開頭中說過吧雹,Volley 是會根據(jù) Android 的版本來選擇對應的 HttpStack骨杂。那么下面我們來深入看一下 HttpStack 的源碼。
public interface HttpStack {
/**
* 通過所給的參數(shù)執(zhí)行請求
*/
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
HttpStack 接口中定義的方法就只有一個雄卷。我們要分別來看看 HurlStack 和 HttpClientStack 各自的實現(xiàn)搓蚪。
HurlStack :
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
// 把請求頭放入 map 中
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
// 使用 HttpURLConnection 來發(fā)起請求
HttpURLConnection connection = openConnection(parsedUrl, request);
// 設置請求頭
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
// 設置請求方法
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
// 把響應封裝進 response 中
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
HttpClientStack :
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
// 根據(jù)請求方法生成對應的 HttpUriRequest
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
// 添加請求頭
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest);
}
在這里只給出 HurlStack 和 HttpClientStack 的 performRequest
方法。我們可以看到 HurlStack 和 HttpClientStack 已經(jīng)把 HttpUrlConnection 和 HttpClient 封裝得很徹底了龙亲,以后哪里有需要的地方可以直接使用陕凹。
RetryPolicy
RetryPolicy 接口主要的作用就是定制重試策略,我們從下面的源碼可以看出該接口有三個抽象方法:
- getCurrentTimeout :得到當前超時時間鳄炉;
- getCurrentRetryCount :得到當前重試的次數(shù)杜耙;
- retry :是否進行重試,其中的
error
參數(shù)為異常的信息拂盯。若在retry
方法中跑出error
異常佑女,那 Volley 就會停止重試。
public interface RetryPolicy {
/**
* Returns the current timeout (used for logging).
*/
public int getCurrentTimeout();
/**
* Returns the current retry count (used for logging).
*/
public int getCurrentRetryCount();
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
* @throws VolleyError In the event that the retry could not be performed (for example if we
* ran out of attempts), the passed in error is thrown.
*/
public void retry(VolleyError error) throws VolleyError;
}
RetryPolicy 接口有一個默認的實現(xiàn)類 DefaultRetryPolicy 谈竿,DefaultRetryPolicy 的構造方法有兩個:
/** The default socket timeout in milliseconds */
public static final int DEFAULT_TIMEOUT_MS = 2500;
/** The default number of retries */
public static final int DEFAULT_MAX_RETRIES = 1;
/** The default backoff multiplier */
public static final float DEFAULT_BACKOFF_MULT = 1f;
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
/**
* Constructs a new retry policy.
* @param initialTimeoutMs The initial timeout for the policy.
* @param maxNumRetries The maximum number of retries.
* @param backoffMultiplier Backoff multiplier for the policy.
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
從上面可以看到团驱,在 Volley 內部已經(jīng)有一套默認的參數(shù)配置了。當然空凸,你也可以通過自定義的形式來設置重試策略嚎花。
@Override
public void retry(VolleyError error) throws VolleyError {
// 重試次數(shù)自增
mCurrentRetryCount++;
// 超時時間自增,mBackoffMultiplier 為超時時間的因子
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
// 如果超過最大次數(shù)呀洲,拋出異常
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
Cache
分析完了前面這么多的類紊选,終于輪到了最后的 Cache 。Cache 接口中定義了一個內部類 Entry 道逗,還有定義了幾個方法:
- get(String key) :根據(jù)傳入的
key
來獲取 entry 兵罢; - put(String key, Entry entry) :增加或者替換緩存;
- initialize() :初始化滓窍,是耗時的操作卖词,在子線程中調用;
- invalidate(String key, boolean fullExpire) :根據(jù)
key
使之對應的緩存失效吏夯; - remove(String key) :根據(jù)
key
移除某個緩存此蜈; - clear() :清空緩存即横。
public interface Cache {
/**
* Retrieves an entry from the cache.
* @param key Cache key
* @return An {@link Entry} or null in the event of a cache miss
*/
public Entry get(String key);
/**
* Adds or replaces an entry to the cache.
* @param key Cache key
* @param entry Data to store and metadata for cache coherency, TTL, etc.
*/
public void put(String key, Entry entry);
/**
* Performs any potentially long-running actions needed to initialize the cache;
* will be called from a worker thread.
*/
public void initialize();
/**
* Invalidates an entry in the cache.
* @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire
*/
public void invalidate(String key, boolean fullExpire);
/**
* Removes an entry from the cache.
* @param key Cache key
*/
public void remove(String key);
/**
* Empties the cache.
*/
public void clear();
}
內部類 Entry ,Entry 中有一個屬性為 etag 舶替,上面的源碼中也有 etag 的身影令境。如果你對 ETag 不熟悉,可以查看這篇文章《Etag與HTTP緩存機制》:
public static class Entry {
/** 緩存中數(shù)據(jù)顾瞪,即響應中的 body */
public byte[] data;
/** HTTP頭部的一個定義,允許客戶端進行緩存協(xié)商 */
public String etag;
/** 服務端響應的時間 */
public long serverDate;
/** 緩存過期的時間抛蚁,若小于當前時間則過期 */
public long ttl;
/** 緩存的有效時間陈醒,若小于當前時間則可以進行刷新操作 */
public long softTtl;
/** 響應的頭部信息 */
public Map<String, String> responseHeaders = Collections.emptyMap();
/** 判斷緩存是否有效,若返回 true 則緩存失效. */
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/** 判斷緩存是否需要刷新 */
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
看完了 Cache 接口之后瞧甩,我們來看一下實現(xiàn)類 DiskBasedCache 钉跷。首先是它的構造方法:
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
mRootDirectory = rootDirectory;
mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}
public DiskBasedCache(File rootDirectory) {
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}
從構造方法中傳入的參數(shù)可知,Volley 默認最大磁盤緩存為 5M 肚逸。
DiskBasedCache 的 get(String key)
方法:
@Override
public synchronized Entry get(String key) {
// 得到對應的緩存摘要信息
CacheHeader entry = mEntries.get(key);
// if the entry does not exist, return.
if (entry == null) {
return null;
}
// 得到緩存文件
File file = getFileForKey(key);
// CountingInputStream 為自定義的 IO 流爷辙,繼承自 FilterInputStream
// 具有記憶已讀取的字節(jié)數(shù)的功能
CountingInputStream cis = null;
try {
cis = new CountingInputStream(new FileInputStream(file));
CacheHeader.readHeader(cis); // eat header
// 得到緩存中的數(shù)據(jù) data[]
byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
return entry.toCacheEntry(data);
} catch (IOException e) {
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
// 若出錯則移除緩存
remove(key);
return null;
} finally {
if (cis != null) {
try {
cis.close();
} catch (IOException ioe) {
return null;
}
}
}
}
// 把 url 分成兩半,分別得到對應的 hashcode 朦促,拼接后得到對應的文件名
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
/**
* Returns a file object for the given cache key.
*/
public File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
DiskBasedCache 的 putEntry(String key, CacheHeader entry)
方法:
@Override
public synchronized void put(String key, Entry entry) {
// 檢查磁盤空間是否足夠膝晾,若不夠會刪除一些緩存文件
pruneIfNeeded(entry.data.length);
File file = getFileForKey(key);
try {
FileOutputStream fos = new FileOutputStream(file);
CacheHeader e = new CacheHeader(key, entry);
e.writeHeader(fos);
fos.write(entry.data);
fos.close();
putEntry(key, e);
return;
} catch (IOException e) {
}
boolean deleted = file.delete();
if (!deleted) {
VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
}
}
private void putEntry(String key, CacheHeader entry) {
// 計算總緩存的大小
if (!mEntries.containsKey(key)) {
mTotalSize += entry.size;
} else {
CacheHeader oldEntry = mEntries.get(key);
mTotalSize += (entry.size - oldEntry.size);
}
// 增加或者替換緩存
mEntries.put(key, entry);
}
initialize()
方法:
@Override
public synchronized void initialize() {
if (!mRootDirectory.exists()) {
if (!mRootDirectory.mkdirs()) {
VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
}
return;
}
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
// 讀取緩存文件
for (File file : files) {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
// 得到緩存文件的摘要信息
CacheHeader entry = CacheHeader.readHeader(fis);
entry.size = file.length();
// 放入 map 中
putEntry(entry.key, entry);
} catch (IOException e) {
if (file != null) {
file.delete();
}
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ignored) { }
}
}
}
invalidate(String key, boolean fullExpire)
方法:
@Override
public synchronized void invalidate(String key, boolean fullExpire) {
Entry entry = get(key);
if (entry != null) {
// 有效時間置零
entry.softTtl = 0;
if (fullExpire) {
entry.ttl = 0;
}
put(key, entry);
}
}
remove(String key)
和 clear()
方法比較簡單,就不需要注釋了:
@Override
public synchronized void remove(String key) {
boolean deleted = getFileForKey(key).delete();
removeEntry(key);
if (!deleted) {
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
key, getFilenameForKey(key));
}
}
@Override
public synchronized void clear() {
File[] files = mRootDirectory.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
}
}
mEntries.clear();
mTotalSize = 0;
VolleyLog.d("Cache cleared.");
}
0100B
至此务冕,Volley 源碼解析差不多已經(jīng)結束了血当。基本上在整個 Volley 框架中至關重要的類都講到了禀忆。當然臊旭,還有一些 NetworkImageView 、ImageLoader 等源碼還沒解析箩退。由于本篇文章內容太長了(有史以來寫過最長的一篇─=≡Σ((( つ??ω??)つ)离熏,只能等到下次有機會再給大家補上了。
在這還給出了一張整個 Volley 框架大致的網(wǎng)絡通信流程圖戴涝,對上面源碼沒看懂的童鞋可以參考這張圖再看一遍:
最后滋戳,只剩下總結了。從頭到尾分析了一遍喊括,發(fā)現(xiàn) Volley 真的是一款很優(yōu)秀的框架胧瓜,面向接口編程在其中發(fā)揮到極致。其中有不少值得我們借鑒的地方郑什,但是 Volley 并不是沒有缺點的府喳,對于大文件傳輸 Volley 就很不擅長,搞不好會 OOM 蘑拯。另外钝满,在源碼中還有不少可以繼續(xù)優(yōu)化的地方兜粘,有興趣的同學可以自定義一個屬于自己的 Volley 。
好了弯蚜,如果你對本文哪里有問題或者不懂的地方孔轴,歡迎留言一起交流。
0101B
References