Android 淺析 Volley (二) 原理

Android 淺析 Volley (二) 原理


前言

Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code

概括

本文通過對Volley源碼進(jìn)行分析來打通Volley的流程疙筹,并且知曉其原理贰剥,Volley的整體結(jié)構(gòu)比較簡單捆愁,但是細(xì)節(jié)有很多值得學(xué)習(xí)的地方。

Volley 原理圖

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.
Calling add(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)用CacheDispatcherstart()函數(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 類分析

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接口返回的值,RequestparseNetworkResponse(…)參數(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)換流程圖
volley response

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

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 流程圖
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篮条。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吩抓,隨后出現(xiàn)的幾起案子涉茧,更是在濱河造成了極大的恐慌,老刑警劉巖疹娶,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伴栓,死亡現(xiàn)場離奇詭異,居然都是意外死亡雨饺,警方通過查閱死者的電腦和手機(jī)钳垮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來额港,“玉大人饺窿,你說我怎么就攤上這事∫普叮” “怎么了肚医?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長向瓷。 經(jīng)常有香客問我肠套,道長,這世上最難降的妖魔是什么猖任? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任你稚,我火速辦了婚禮,結(jié)果婚禮上朱躺,老公的妹妹穿的比我還像新娘刁赖。我一直安慰自己,他們只是感情好长搀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布乾闰。 她就那樣靜靜地躺著,像睡著了一般盈滴。 火紅的嫁衣襯著肌膚如雪涯肩。 梳的紋絲不亂的頭發(fā)上轿钠,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機(jī)與錄音病苗,去河邊找鬼疗垛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛硫朦,可吹牛的內(nèi)容都是我干的贷腕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咬展,長吁一口氣:“原來是場噩夢啊……” “哼泽裳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起破婆,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤涮总,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后祷舀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瀑梗,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年裳扯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抛丽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饰豺,死狀恐怖亿鲜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冤吨,我是刑警寧澤狡门,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站锅很,受9級特大地震影響其馏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜爆安,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一叛复、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扔仓,春花似錦褐奥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至版保,卻和暖如春呜笑,著一層夾襖步出監(jiān)牢的瞬間夫否,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工叫胁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凰慈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓驼鹅,卻偏偏與公主長得像微谓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子输钩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評論 2 354

推薦閱讀更多精彩內(nèi)容