Android 淺析 Volley (二) 原理
前言
Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code
概括
本文通過對Volley源碼進行分析來打通Volley的流程,并且知曉其原理,Volley的整體結構比較簡單,但是細節有很多值得學習的地方。
Volley初始化
RequestQueue mQueue = Volley.newRequestQueue(this);
Volley的初始化就是簡單的一行代碼,但是里面做的事情比較復雜了。
Volley
這是Volley系統最主要的類之一,它用來初始化緩存目錄和初始化下載響應隊列。
Volley.newRequestQueue(...)
Creates a default instance of the worker pool and calls
RequestQueue.start()
on it.
HttpStack stack = new HurlStack();
Network network = new BasicNetwork(stack);
File cacheDir = new File(...);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
這個函數不難,首先創建一個HttpStack
對象和創建一個緩存文件,然后用創建的對象生成一個RequestQueue
響應隊列,最后調用RequestQueue.start()
函數開始運行。
那么我們開始深入去看下,首先看下RequestQueue。
RequestQueue
A request dispatch queue with a thread pool of dispatchers.
Callingadd(Request)
will enqueue the given Request for dispatch, resolving from either cache or network on a worker thread, and then delivering a parsed response on the main thread.
RequestQueue.RequestQueue(...)
Creates the worker pool.
初始化這里有幾個關鍵的點:
1、DEFAULT_NETWORK_THREAD_POOL_SIZE:訪問網絡的線程數,默認是4條線程。
2、new ExecutorDelivery(new Handler(Looper.getMainLooper())):新建一個用來推出網絡響應和錯誤的類。
3、new NetworkDispatcher[threadPoolSize]:新建四條網絡連接的線程。
//@param cache A Cache to use for persisting responses to disk
//@param network A Network interface for performing HTTP requests
//@param threadPoolSize Number of network dispatcher threads to create
//@param delivery A ResponseDelivery interface for posting responses and errors
RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery)
RequestQueue.Start()
Starts the dispatchers in this queue.
首先是創建一個CacheDispatcher
緩存發送者,它是一個緩存發送的線程,然后調用CacheDispatcher
的start()
函數。
然后循環創建NetworkDispatcher
對象,因為默認的線程數是4,所以會循環4次創建網絡調度(和相應的線程)到池大小,接著再調用start()
函數。
簡單來說當調用了Volley.newRequestQueue(context)之后,就會有五個線程一直在后臺運行,不斷等待網絡請求的到來,其中CacheDispatcher是緩存線程,NetworkDispatcher是網絡請求線程。
RequestQueue.Stop()
Stops the cache and network dispatchers.
將所有緩存和網絡發送者停止。
總結
通過這幾行簡單的函數就創建了Volley的緩存和網絡發送者隊列,為以后的請求建立了機制。
Volley添加請求
Volley的添加請求也很簡單。
StringRequest stringRequest = new StringRequest(...);
mQueue.add(stringRequest);
創建一個Request對象并且添加進Queue隊列就行了。看起來簡單,實際,好吧,很復雜。
RequestQueue
RequestQueue.add()
這里函數本身比較簡單易懂,首先將Request標識為當前隊列,然后添加到mCurrentRequests
當前請求隊列里。接著判斷是否可以緩存,如果不行則直接調用網絡隊列進行請求。最后插入請求到現階段,如果已經有一個緩存鍵的請求在進行。
總結
添加的操作是比較簡單,但難的是里面的具體過程,緩存請求和網絡請求都是獨立線程,在添加到隊列之后就會不斷的去獲取然后對網絡或者緩存進行處理。
Volley 類分析
工具類
網絡類
HttpStack
Performs an HTTP request with the given parameters.
用于處理 Http 請求,返回請求結果的接口。
HurlStack
based on HttpURLConnection.
實現 HttpStack 接口,基于HttpURLConnection進行各種請求方式的請求封裝。
BasicNetwork
A network performing Volley requests over an HttpStack.
一個基于HttpStack執行Volley請求的網絡。
調用HttpStack處理請求,并將結果轉換為可被ResponseDelivery處理的NetworkResponse。
主要實現了以下功能:
1、利用 HttpStack 執行網絡請求。
2、如果 Request 中帶有實體信息,如 Etag,Last-Modify 等,則進行緩存新鮮度的驗證,并處理 304(Not Modify)響應。
3、如果發生超時,認證失敗等錯誤,進行重試操作,直到成功、拋出異常(不滿足重試策略等)結束。
Network
An interface for performing requests.
接口類,作為網絡請求的主要接口,傳入需要訪問的執行信息,并且返回一個NetworkResponse
網絡響應對象。
緩存類
Cache
An interface for a cache keyed by a String with a byte array as data.
接口,一個可以獲取請求結果,存儲請求結果的緩存。
Entry get(String key)
通過 key 獲取請求的緩存實體
void put(String key, Entry entry)
存入一個請求的緩存實體
void remove(String key)
移除指定的緩存實體
void clear()
清空緩存
Entry內部類
byte[] data : 請求返回的數據(Body 實體)
String etag Http : 響應首部中用于緩存新鮮度驗證的 ETag
long serverDate Http : 響應首部中的響應產生時間
long ttl : 緩存的過期時間
long softTtl : 緩存的新鮮時間
Map<String, String> responseHeaders : 響應的 Headers
boolean isExpired() : 判斷緩存是否過期,過期緩存不能繼續使用
boolean refreshNeeded() : 判斷緩存是否新鮮,不新鮮的緩存需要發到服務端做新鮮度的檢測
NoCache
什么緩存都沒有。不做任何操作的緩存實現類,可將它作為構建RequestQueue的參數以實現一個不帶緩存的請求隊列。
DiskBasedCache
Cache implementation that caches files directly onto the hard disk in the specified directory. The default disk usage size is 5MB, but is configurable.
void initialize()
Initializes the DiskBasedCache by scanning for all files currently in the specified root directory. Creates the root directory if necessary.
初始化,掃描緩存目錄得到所有緩存數據摘要信息放入內存。
Entry get(String key)
Returns the cache entry with the specified key if it exists, null otherwise.
從緩存中得到數據。先從緩存頭中得到頭部信息,然后讀取緩存數據文件得到內容。
void put(String key, Entry entry)
Puts the entry with the specified key into the cache.
將數據文件內容保存到緩存。先檢查緩存是否已滿,已滿則先刪除緩存中部分數據,然后再新建緩存文件。
void pruneIfNeeded(int neededSpace)
Prunes the cache to fit the amount of bytes specified.
檢查是否能再分配 neededSpace 字節的空間,如果不能則刪除緩存中部分數據。
void clear()
Clears the cache. Deletes all cached files from disk.
清空緩存。
void remove(String key)
Removes the specified key from the cache if it exists.
刪除緩存中某個元素。
CacheHeader
Handles holding onto the cache headers for an entry.
緩存文件頭部信息在entry里。
ByteArrayPool
ByteArrayPool is a source and repository of
byte[]
objects.
byte[] 的回收池,用于 byte[] 的回收再利用,減少了內存的分配和回收。 主要通過一個元素長度從小到大排序的ArrayList作為 byte[] 的緩存,另有一個按使用時間先后排序的ArrayList屬性用于緩存滿時清理元素。
PoolingByteArrayOutputStream
A variation of
java.io.ByteArrayOutputStream
that uses a pool of byte[] buffers instead of always allocating them fresh, saving on heap churn.
Authenticator
身份認證接口,用于基本認證或者摘要認證。這個類是 Volley 用于和身份驗證打通的接口,比如 OAuth,不過目前的使用不是特別廣泛和 Volley 的內部結合也不是特別緊密。
AndroidAuthenticator
繼承 Authenticator,基于 Android AccountManager 的認證交互實現類。
基礎類
NetworkResponse
Data and headers returned from performRequest(Request).
作為Network
接口返回的值,Request
的parseNetworkResponse(…)
參數,是 Volley 中用于內部 Response 轉換的核心類。
封裝了網絡請求響應的 StatusCode,Headers 和 Body 等。
成員變量
int statusCode
Http 響應狀態碼
byte[] data
Body 數據
Map<String, String> headers
響應 Headers
boolean notModified
表示是否為 304 響應
long networkTimeMs
請求耗時
內部 Response 轉換流程圖
CacheDispatcher
Provides a thread for performing cache triage on a queue of requests.
CacheDispatcher類是一個線程類,里面有四個比較重要的變量:
1、BlockingQueue<Request<?>> mCacheQueue
來自緩存的隊列。
2、BlockingQueue<Request<?>> mNetworkQueue
將會訪問網絡的隊列。
3、Cache mCache
緩存的處理類。
4、ResponseDelivery mDelivery
回調響應的類。
CacheDispatcher.run()
在線程被創建后馬上就被執行了。里面首先會對緩存路徑進行初始化工作。接著就進入一個無限的循環等待里。
首先會從緩存隊列出取出請求,如果隊列為空則會一直阻塞直到隊列有數據,接著嘗試從緩存當中取出響應結果,如何為空的話則把這條請求加入到網絡請求隊列中,如果不為空的話再判斷該緩存是否已過期,如果已經過期了則同樣把這條請求加入到網絡請求隊列中,否則就認為不需要重發網絡請求,直接使用緩存中的數據即可。之后會調用Request的parseNetworkResponse()方法來對數據進行解析,最后就是將解析出來的數據進行回調了。
CacheDispatcher 流程圖
NetworkDispatcher
Provides a thread for performing network dispatch from a queue of requests.
NetworkDispatcher類是一個線程類,里面有四個比較重要的變量:
1、BlockingQueue<Request<?>> mQueue響應來自服務的隊列。
2、Network mNetwork 網絡進度請求。
3、Cache mCache緩存的處理類。
4、ResponseDelivery mDelivery回調響應的類。
NetworkDispatcher.run()
在線程被創建后馬上就被執行了。首先進入一個無限的循環等待里。等待任務進來。
在接受到請求后會直接訪問網絡發送請求,請求處理結束則將結果傳遞給ResponseDelivery去執行后續處理,并判斷結果是否要進行緩存。
NetworkDispatcher 流程圖
ResponseDelivery
一個返回結果的分發接口。
有三個分發接口:
1、解析一個從網絡或緩存的響應并分發。
postResponse(Request<?> request, Response<?> response);
2、解析一個從網絡或緩存的響應并分發。提供的運行將會在分發后被執行。
postResponse(Request<?> request, Response<?> response, Runnable runnable);
3、推送一個收到的錯誤。
postError(Request<?> request, VolleyError error);
RetryPolicy
Retry policy for a request.
void retry(VolleyError error) throws VolleyError
確定是否重試,參數為這次異常的具體信息。在請求異常時此接口會被調用,可在此函數實現中拋出傳入的異常表示停止重試。
DefaultRetryPolicy
Default retry policy for requests.
實現 RetryPolicy,Volley 默認的重試策略實現類。主要通過在 retry(…) 函數中判斷重試次數是否達到上限確定是否繼續重試。
其中mCurrentRetryCount變量表示已經重試次數。
mBackoffMultiplier表示每次重試之前的 timeout 該乘以的因子。
mCurrentTimeoutMs變量表示當前重試的 timeout 時間,會以mBackoffMultiplier作為因子累計前幾次重試的 timeout。