? ? ? 本文主要從源碼的角度分析 Volley 的原理。
? ? ? 我們在使用Volley的時候,首先需要通過 Volley.newRequestQueue(mContext) 獲取到RequestQueue的一個實例,我們就以此為入口,開始分析。看一下newRequestQueue的實現(xiàn)代碼

? ? ? ? ?在Volley.newRequestQueue(mContext)方法中調(diào)用重載方法,一直調(diào)用到newRequestQueue(Context context,HttpStack stack, intmaxDiskCacheBytes) 方法。我們看一下這里的實現(xiàn),代碼如下:

從第55開始,對stack進行了賦值操作,當sdk version 大于等于9 的是時候使用了HurlStack初始化,否則使用HttpClientStack初始化,這里其實就是根據(jù)系統(tǒng)版本選擇了網(wǎng)絡通訊的方式,當Android系統(tǒng)版本大于等于2.3時使用了?HurlStack(內(nèi)部由HttpURLConnection實現(xiàn)),小于2.3版本使用HttpClientStack(內(nèi)部由HttpClient實現(xiàn))。
這里之所以這樣實現(xiàn),有三個原因:
1.HttpClient 的API 很多,穩(wěn)定,易操作,但擴展性差。
2.HttpURLConnection 相比HttpClient API比較簡單,擴展性很好。
3.HttpURLConnection在2.3版本之前存在嚴重bug?
好了,回歸正題,創(chuàng)建好之后stack之后,在第65行創(chuàng)建了Network的一個實例,Network是用于處理網(wǎng)絡請求的,大家可以看下Network 源碼中的performRequest方法,緊接著從67行開始,創(chuàng)建了RequestQueue對象,并調(diào)用了RequestQueue 的start方法,這里很重要,我們看下代碼:

在這個方法中 有兩個比較關鍵的類CacheDispatcher和NetworkDispatcher,CacheDispatcher和NetworkDispatcher都是繼承自Thread。我們可以看到,這里實例化了一個CacheDispatcher線程和若干個NetworkDispatcher線程,并執(zhí)行了start方法。
到這里我們知道,當初始化RequestQueue的時候會啟動5個線程,1個緩存線程,默認4個網(wǎng)絡線程。既然是線程我們看一下CacheDispatcher和NetworkDispatcher的run方法,首先看下NetworkDispatcher的run方法

大家可以看到在86行的地方是while(true),死循環(huán),說明這個線程會一直執(zhí)行,有點意思,我們繼續(xù)往下看,在第92行的地方,我們從隊列中拿到一個request,并且在第114行的地方 調(diào)用mNetwork.performRequest(request);發(fā)起網(wǎng)絡請求,到這里 就完成了從網(wǎng)絡獲取數(shù)據(jù)的操作。
之后在125行,調(diào)用了request.parseNetworkResponse 對數(shù)據(jù)進行解析,在130行處,寫入緩存,然后在148行代碼處調(diào)用了mDelivery.postResponse方法 回調(diào)數(shù)據(jù)。
Volley 提供的StringRequest,JsonRequest 包括我們自定義的request,都是繼承自Request,Request中有兩個方法是我們必須要重寫的,一個是parseNetworkResponse解析數(shù)據(jù),另一個是deliverResponse回調(diào)。
parseNetworkResponse
這個方法主要是用來解析數(shù)據(jù)的,在114行代碼處拿到網(wǎng)絡相應NetworkResponse后,在125行代碼 ,調(diào)用了parseNetworkResponse方法,數(shù)據(jù)解析完成之后,返回了一個Response對象。

拿到Response對象之后 在137行代碼調(diào)用了ExecutorDelivery的postResponse方法回調(diào)數(shù)據(jù),我們看下這里的實現(xiàn)
ExecutorDelivery

這里在postResponse方法中調(diào)用了mResponsePoster的execute 方法并傳入了一個ResponseDeliveryRunnable對象,我們先看下mResponsePoster的實現(xiàn),代碼如下:

看到這里我想大家都應該明白了,handler.post 使上文傳進去的Runnable對象中的run方法 在主線程中執(zhí)行。
我們繼續(xù)看ResponseDeliveryRunnable的實現(xiàn),代碼如下:

我們只看核心代碼,第99行調(diào)用了mRequest.deliverResponse方法,這樣最終回調(diào)到了request的deliverResponse方法,我們可以在這里將數(shù)據(jù)回調(diào)到Response.Listener中的onResponse()方法。代碼如下:

現(xiàn)在我再看下CacheDispatcher的run方法,代碼如下:


和NetworkDispatcher一樣?CacheDispatcher 也啟動了一個死循環(huán),代碼92行,從緩存隊列中獲取request,在100行通過mCache.get(request.getCacheKey());從緩存中獲取結果,如果為空或者已過期則將請求加入網(wǎng)絡隊列中,在代碼132處,判斷了代碼的新鮮度,如果數(shù)據(jù)不需要刷新則回調(diào)數(shù)據(jù),如果需要刷新,則回調(diào)緩存數(shù)據(jù)之后將請求加入網(wǎng)絡隊列中。
wait...好像漏了點什么,隊列中的請求哪里來?
Volley.newRequestQueue(mContext).add(request)
當我們發(fā)起一個請求的時候,是需要RequestQueue.add 方法添加一個request,我們看一下add方法的實現(xiàn):

在代碼第239行,判斷了該request是否需要緩存,不需要則將請求加入網(wǎng)絡隊列中,否則249行代碼,判斷該請求是否在等待請求集合中,沒有則將其加入緩存隊列中。
上文我們已經(jīng)提到NetworkDispatcher和CacheDispatcher 里面都是死循環(huán),一直在等待request,這樣我們add一個請求之后,相應的線程就會開始處理改請求。
基本到這里已經(jīng)結束了。說幾個關鍵點。
緩存空間大小不足
默認情況Volley提供的緩存文件大小是5M,如果超過5M怎么辦?當然Volley給了解決辦法,我們看下源碼,在DiskBasedCache 中pruneIfNeeded方法

neededSpace 是本次申請的空間大小,首先 判斷 neededSpace 和目前已經(jīng)占用的空間總和 是否小于最大空間,如果小于則不處理,否則會遍歷 本地的緩存文件,并進行刪除,直到已占用空間(包括本次申請的neededSpace)小于最大空間*HYSTERESIS_FACTOR,HYSTERESIS_FACTOR的值是0.9,主要是容錯處理。
另外,這里的刪除算法,存在優(yōu)化空間,目前Volley Map進行遍歷,然后依次刪除,可能這一秒我剛緩存的數(shù)據(jù),下一秒就被刪除掉了,一些未過期的數(shù)據(jù)被刪除,已過期的數(shù)據(jù)還依然保留的情況。其實可以先刪除已過期的數(shù)據(jù),在刪除最久未使用的數(shù)據(jù)(LRU)