Android 淺析 Volley (二) 原理
前言
Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code
概括
本文通過對Volley源碼進(jìn)行分析來打通Volley的流程疙筹,并且知曉其原理贰剥,Volley的整體結(jié)構(gòu)比較簡單捆愁,但是細(xì)節(jié)有很多值得學(xué)習(xí)的地方。
Volley初始化
RequestQueue mQueue = Volley.newRequestQueue(this);
Volley的初始化就是簡單的一行代碼揣钦,但是里面做的事情比較復(fù)雜了。
Volley
這是Volley系統(tǒng)最主要的類之一,它用來初始化緩存目錄和初始化下載響應(yīng)隊(duì)列。
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();
這個(gè)函數(shù)不難轻腺,首先創(chuàng)建一個(gè)HttpStack
對象和創(chuàng)建一個(gè)緩存文件,然后用創(chuàng)建的對象生成一個(gè)RequestQueue
響應(yīng)隊(duì)列划乖,最后調(diào)用RequestQueue.start()
函數(shù)開始運(yùn)行贬养。
那么我們開始深入去看下,首先看下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.
初始化這里有幾個(gè)關(guān)鍵的點(diǎn):
1误算、DEFAULT_NETWORK_THREAD_POOL_SIZE:訪問網(wǎng)絡(luò)的線程數(shù),默認(rèn)是4條線程迷殿。
2儿礼、new ExecutorDelivery(new Handler(Looper.getMainLooper())):新建一個(gè)用來推出網(wǎng)絡(luò)響應(yīng)和錯(cuò)誤的類。
3庆寺、new NetworkDispatcher[threadPoolSize]:新建四條網(wǎng)絡(luò)連接的線程蚊夫。
//@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.
首先是創(chuàng)建一個(gè)CacheDispatcher
緩存發(fā)送者,它是一個(gè)緩存發(fā)送的線程懦尝,然后調(diào)用CacheDispatcher
的start()
函數(shù)知纷。
然后循環(huán)創(chuàng)建NetworkDispatcher
對象,因?yàn)槟J(rèn)的線程數(shù)是4陵霉,所以會循環(huán)4次創(chuàng)建網(wǎng)絡(luò)調(diào)度(和相應(yīng)的線程)到池大小琅轧,接著再調(diào)用start()
函數(shù)。
簡單來說當(dāng)調(diào)用了Volley.newRequestQueue(context)之后撩匕,就會有五個(gè)線程一直在后臺運(yùn)行鹰晨,不斷等待網(wǎng)絡(luò)請求的到來,其中CacheDispatcher是緩存線程止毕,NetworkDispatcher是網(wǎng)絡(luò)請求線程模蜡。
RequestQueue.Stop()
Stops the cache and network dispatchers.
將所有緩存和網(wǎng)絡(luò)發(fā)送者停止。
總結(jié)
通過這幾行簡單的函數(shù)就創(chuàng)建了Volley的緩存和網(wǎng)絡(luò)發(fā)送者隊(duì)列扁凛,為以后的請求建立了機(jī)制忍疾。
Volley添加請求
Volley的添加請求也很簡單。
StringRequest stringRequest = new StringRequest(...);
mQueue.add(stringRequest);
創(chuàng)建一個(gè)Request對象并且添加進(jìn)Queue隊(duì)列就行了谨朝÷倍剩看起來簡單,實(shí)際字币,好吧则披,很復(fù)雜。
RequestQueue
RequestQueue.add()
這里函數(shù)本身比較簡單易懂洗出,首先將Request標(biāo)識為當(dāng)前隊(duì)列士复,然后添加到mCurrentRequests
當(dāng)前請求隊(duì)列里。接著判斷是否可以緩存,如果不行則直接調(diào)用網(wǎng)絡(luò)隊(duì)列進(jìn)行請求阱洪。最后插入請求到現(xiàn)階段便贵,如果已經(jīng)有一個(gè)緩存鍵的請求在進(jìn)行。
總結(jié)
添加的操作是比較簡單冗荸,但難的是里面的具體過程承璃,緩存請求和網(wǎng)絡(luò)請求都是獨(dú)立線程,在添加到隊(duì)列之后就會不斷的去獲取然后對網(wǎng)絡(luò)或者緩存進(jìn)行處理蚌本。
Volley 類分析
工具類
網(wǎng)絡(luò)類
HttpStack
Performs an HTTP request with the given parameters.
用于處理 Http 請求盔粹,返回請求結(jié)果的接口。
HurlStack
based on HttpURLConnection.
實(shí)現(xiàn) HttpStack 接口程癌,基于HttpURLConnection進(jìn)行各種請求方式的請求封裝玻佩。
BasicNetwork
A network performing Volley requests over an HttpStack.
一個(gè)基于HttpStack執(zhí)行Volley請求的網(wǎng)絡(luò)。
調(diào)用HttpStack處理請求席楚,并將結(jié)果轉(zhuǎn)換為可被ResponseDelivery處理的NetworkResponse咬崔。
主要實(shí)現(xiàn)了以下功能:
1、利用 HttpStack 執(zhí)行網(wǎng)絡(luò)請求烦秩。
2垮斯、如果 Request 中帶有實(shí)體信息,如 Etag,Last-Modify 等只祠,則進(jìn)行緩存新鮮度的驗(yàn)證兜蠕,并處理 304(Not Modify)響應(yīng)。
3抛寝、如果發(fā)生超時(shí)熊杨,認(rèn)證失敗等錯(cuò)誤,進(jìn)行重試操作盗舰,直到成功晶府、拋出異常(不滿足重試策略等)結(jié)束。
Network
An interface for performing requests.
接口類钻趋,作為網(wǎng)絡(luò)請求的主要接口川陆,傳入需要訪問的執(zhí)行信息,并且返回一個(gè)NetworkResponse
網(wǎng)絡(luò)響應(yīng)對象蛮位。
緩存類
Cache
An interface for a cache keyed by a String with a byte array as data.
接口较沪,一個(gè)可以獲取請求結(jié)果,存儲請求結(jié)果的緩存失仁。
Entry get(String key)
通過 key 獲取請求的緩存實(shí)體
void put(String key, Entry entry)
存入一個(gè)請求的緩存實(shí)體
void remove(String key)
移除指定的緩存實(shí)體
void clear()
清空緩存
Entry內(nèi)部類
byte[] data : 請求返回的數(shù)據(jù)(Body 實(shí)體)
String etag Http : 響應(yīng)首部中用于緩存新鮮度驗(yàn)證的 ETag
long serverDate Http : 響應(yīng)首部中的響應(yīng)產(chǎn)生時(shí)間
long ttl : 緩存的過期時(shí)間
long softTtl : 緩存的新鮮時(shí)間
Map<String, String> responseHeaders : 響應(yīng)的 Headers
boolean isExpired() : 判斷緩存是否過期尸曼,過期緩存不能繼續(xù)使用
boolean refreshNeeded() : 判斷緩存是否新鮮,不新鮮的緩存需要發(fā)到服務(wù)端做新鮮度的檢測
NoCache
什么緩存都沒有萄焦。不做任何操作的緩存實(shí)現(xiàn)類控轿,可將它作為構(gòu)建RequestQueue的參數(shù)以實(shí)現(xiàn)一個(gè)不帶緩存的請求隊(duì)列。
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.
初始化,掃描緩存目錄得到所有緩存數(shù)據(jù)摘要信息放入內(nèi)存解幽。
Entry get(String key)
Returns the cache entry with the specified key if it exists, null otherwise.
從緩存中得到數(shù)據(jù)。先從緩存頭中得到頭部信息烘苹,然后讀取緩存數(shù)據(jù)文件得到內(nèi)容躲株。
void put(String key, Entry entry)
Puts the entry with the specified key into the cache.
將數(shù)據(jù)文件內(nèi)容保存到緩存。先檢查緩存是否已滿镣衡,已滿則先刪除緩存中部分?jǐn)?shù)據(jù)霜定,然后再新建緩存文件。
void pruneIfNeeded(int neededSpace)
Prunes the cache to fit the amount of bytes specified.
檢查是否能再分配 neededSpace 字節(jié)的空間廊鸥,如果不能則刪除緩存中部分?jǐn)?shù)據(jù)望浩。
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.
刪除緩存中某個(gè)元素惰说。
CacheHeader
Handles holding onto the cache headers for an entry.
緩存文件頭部信息在entry里磨德。
ByteArrayPool
ByteArrayPool is a source and repository of
byte[]
objects.
byte[] 的回收池,用于 byte[] 的回收再利用吆视,減少了內(nèi)存的分配和回收典挑。 主要通過一個(gè)元素長度從小到大排序的ArrayList作為 byte[] 的緩存,另有一個(gè)按使用時(shí)間先后排序的ArrayList屬性用于緩存滿時(shí)清理元素啦吧。
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
身份認(rèn)證接口您觉,用于基本認(rèn)證或者摘要認(rèn)證。這個(gè)類是 Volley 用于和身份驗(yàn)證打通的接口授滓,比如 OAuth琳水,不過目前的使用不是特別廣泛和 Volley 的內(nèi)部結(jié)合也不是特別緊密。
AndroidAuthenticator
繼承 Authenticator般堆,基于 Android AccountManager 的認(rèn)證交互實(shí)現(xiàn)類在孝。
基礎(chǔ)類
NetworkResponse
Data and headers returned from performRequest(Request).
作為Network
接口返回的值,Request
的parseNetworkResponse(…)
參數(shù)淮摔,是 Volley 中用于內(nèi)部 Response 轉(zhuǎn)換的核心類浑玛。
封裝了網(wǎng)絡(luò)請求響應(yīng)的 StatusCode,Headers 和 Body 等噩咪。
成員變量
int statusCode
Http 響應(yīng)狀態(tài)碼
byte[] data
Body 數(shù)據(jù)
Map<String, String> headers
響應(yīng) Headers
boolean notModified
表示是否為 304 響應(yīng)
long networkTimeMs
請求耗時(shí)
內(nèi)部 Response 轉(zhuǎn)換流程圖
CacheDispatcher
Provides a thread for performing cache triage on a queue of requests.
CacheDispatcher類是一個(gè)線程類顾彰,里面有四個(gè)比較重要的變量:
1、BlockingQueue<Request<?>> mCacheQueue
來自緩存的隊(duì)列胃碾。
2涨享、BlockingQueue<Request<?>> mNetworkQueue
將會訪問網(wǎng)絡(luò)的隊(duì)列。
3仆百、Cache mCache
緩存的處理類厕隧。
4、ResponseDelivery mDelivery
回調(diào)響應(yīng)的類。
CacheDispatcher.run()
在線程被創(chuàng)建后馬上就被執(zhí)行了吁讨。里面首先會對緩存路徑進(jìn)行初始化工作髓迎。接著就進(jìn)入一個(gè)無限的循環(huán)等待里。
首先會從緩存隊(duì)列出取出請求建丧,如果隊(duì)列為空則會一直阻塞直到隊(duì)列有數(shù)據(jù)排龄,接著嘗試從緩存當(dāng)中取出響應(yīng)結(jié)果,如何為空的話則把這條請求加入到網(wǎng)絡(luò)請求隊(duì)列中翎朱,如果不為空的話再判斷該緩存是否已過期橄维,如果已經(jīng)過期了則同樣把這條請求加入到網(wǎng)絡(luò)請求隊(duì)列中,否則就認(rèn)為不需要重發(fā)網(wǎng)絡(luò)請求拴曲,直接使用緩存中的數(shù)據(jù)即可争舞。之后會調(diào)用Request的parseNetworkResponse()方法來對數(shù)據(jù)進(jìn)行解析,最后就是將解析出來的數(shù)據(jù)進(jìn)行回調(diào)了澈灼。
CacheDispatcher 流程圖
NetworkDispatcher
Provides a thread for performing network dispatch from a queue of requests.
NetworkDispatcher類是一個(gè)線程類竞川,里面有四個(gè)比較重要的變量:
1、BlockingQueue<Request<?>> mQueue響應(yīng)來自服務(wù)的隊(duì)列叁熔。
2流译、Network mNetwork 網(wǎng)絡(luò)進(jìn)度請求。
3者疤、Cache mCache緩存的處理類福澡。
4、ResponseDelivery mDelivery回調(diào)響應(yīng)的類驹马。
NetworkDispatcher.run()
在線程被創(chuàng)建后馬上就被執(zhí)行了革砸。首先進(jìn)入一個(gè)無限的循環(huán)等待里。等待任務(wù)進(jìn)來糯累。
在接受到請求后會直接訪問網(wǎng)絡(luò)發(fā)送請求算利,請求處理結(jié)束則將結(jié)果傳遞給ResponseDelivery去執(zhí)行后續(xù)處理,并判斷結(jié)果是否要進(jìn)行緩存泳姐。
NetworkDispatcher 流程圖
ResponseDelivery
一個(gè)返回結(jié)果的分發(fā)接口效拭。
有三個(gè)分發(fā)接口:
1、解析一個(gè)從網(wǎng)絡(luò)或緩存的響應(yīng)并分發(fā)胖秒。
postResponse(Request<?> request, Response<?> response);
2缎患、解析一個(gè)從網(wǎng)絡(luò)或緩存的響應(yīng)并分發(fā)。提供的運(yùn)行將會在分發(fā)后被執(zhí)行阎肝。
postResponse(Request<?> request, Response<?> response, Runnable runnable);
3挤渔、推送一個(gè)收到的錯(cuò)誤。
postError(Request<?> request, VolleyError error);
RetryPolicy
Retry policy for a request.
void retry(VolleyError error) throws VolleyError
確定是否重試风题,參數(shù)為這次異常的具體信息判导。在請求異常時(shí)此接口會被調(diào)用嫉父,可在此函數(shù)實(shí)現(xiàn)中拋出傳入的異常表示停止重試。
DefaultRetryPolicy
Default retry policy for requests.
實(shí)現(xiàn) RetryPolicy眼刃,Volley 默認(rèn)的重試策略實(shí)現(xiàn)類绕辖。主要通過在 retry(…) 函數(shù)中判斷重試次數(shù)是否達(dá)到上限確定是否繼續(xù)重試。
其中mCurrentRetryCount變量表示已經(jīng)重試次數(shù)擂红。
mBackoffMultiplier表示每次重試之前的 timeout 該乘以的因子仪际。
mCurrentTimeoutMs變量表示當(dāng)前重試的 timeout 時(shí)間,會以mBackoffMultiplier作為因子累計(jì)前幾次重試的 timeout篮条。