Volley 原理解析

? ? ? 本文主要從源碼的角度分析 Volley 的原理。

? ? ? 我們在使用Volley的時候,首先需要通過 Volley.newRequestQueue(mContext) 獲取到RequestQueue的一個實例,我們就以此為入口,開始分析。看一下newRequestQueue的實現代碼

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

從第55開始,對stack進行了賦值操作,當sdk version 大于等于9 的是時候使用了HurlStack初始化,否則使用HttpClientStack初始化,這里其實就是根據系統版本選擇了網絡通訊的方式,當Android系統版本大于等于2.3時使用了?HurlStack(內部由HttpURLConnection實現),小于2.3版本使用HttpClientStack(內部由HttpClient實現)。

這里之所以這樣實現,有三個原因:

1.HttpClient 的API 很多,穩定,易操作,但擴展性差。

2.HttpURLConnection 相比HttpClient API比較簡單,擴展性很好。

3.HttpURLConnection在2.3版本之前存在嚴重bug?

好了,回歸正題,創建好之后stack之后,在第65行創建了Network的一個實例,Network是用于處理網絡請求的,大家可以看下Network 源碼中的performRequest方法,緊接著從67行開始,創建了RequestQueue對象,并調用了RequestQueue 的start方法,這里很重要,我們看下代碼:


在這個方法中 有兩個比較關鍵的類CacheDispatcher和NetworkDispatcher,CacheDispatcher和NetworkDispatcher都是繼承自Thread。我們可以看到,這里實例化了一個CacheDispatcher線程和若干個NetworkDispatcher線程,并執行了start方法。

到這里我們知道,當初始化RequestQueue的時候會啟動5個線程,1個緩存線程,默認4個網絡線程。既然是線程我們看一下CacheDispatcher和NetworkDispatcher的run方法,首先看下NetworkDispatcher的run方法


大家可以看到在86行的地方是while(true),死循環,說明這個線程會一直執行,有點意思,我們繼續往下看,在第92行的地方,我們從隊列中拿到一個request,并且在第114行的地方 調用mNetwork.performRequest(request);發起網絡請求,到這里 就完成了從網絡獲取數據的操作。

之后在125行,調用了request.parseNetworkResponse 對數據進行解析,在130行處,寫入緩存,然后在148行代碼處調用了mDelivery.postResponse方法 回調數據。

Volley 提供的StringRequest,JsonRequest 包括我們自定義的request,都是繼承自Request,Request中有兩個方法是我們必須要重寫的,一個是parseNetworkResponse解析數據,另一個是deliverResponse回調。

parseNetworkResponse

這個方法主要是用來解析數據的,在114行代碼處拿到網絡相應NetworkResponse后,在125行代碼 ,調用了parseNetworkResponse方法,數據解析完成之后,返回了一個Response對象。

拿到Response對象之后 在137行代碼調用了ExecutorDelivery的postResponse方法回調數據,我們看下這里的實現

ExecutorDelivery


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

看到這里我想大家都應該明白了,handler.post 使上文傳進去的Runnable對象中的run方法 在主線程中執行。

我們繼續看ResponseDeliveryRunnable的實現,代碼如下:

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

現在我再看下CacheDispatcher的run方法,代碼如下:


和NetworkDispatcher一樣?CacheDispatcher 也啟動了一個死循環,代碼92行,從緩存隊列中獲取request,在100行通過mCache.get(request.getCacheKey());從緩存中獲取結果,如果為空或者已過期則將請求加入網絡隊列中,在代碼132處,判斷了代碼的新鮮度,如果數據不需要刷新則回調數據,如果需要刷新,則回調緩存數據之后將請求加入網絡隊列中。

wait...好像漏了點什么,隊列中的請求哪里來?

Volley.newRequestQueue(mContext).add(request)

當我們發起一個請求的時候,是需要RequestQueue.add 方法添加一個request,我們看一下add方法的實現:

在代碼第239行,判斷了該request是否需要緩存,不需要則將請求加入網絡隊列中,否則249行代碼,判斷該請求是否在等待請求集合中,沒有則將其加入緩存隊列中。

上文我們已經提到NetworkDispatcher和CacheDispatcher 里面都是死循環,一直在等待request,這樣我們add一個請求之后,相應的線程就會開始處理改請求。

基本到這里已經結束了。說幾個關鍵點。

緩存空間大小不足

默認情況Volley提供的緩存文件大小是5M,如果超過5M怎么辦?當然Volley給了解決辦法,我們看下源碼,在DiskBasedCache 中pruneIfNeeded方法

neededSpace 是本次申請的空間大小,首先 判斷 neededSpace 和目前已經占用的空間總和 是否小于最大空間,如果小于則不處理,否則會遍歷 本地的緩存文件,并進行刪除,直到已占用空間(包括本次申請的neededSpace)小于最大空間*HYSTERESIS_FACTOR,HYSTERESIS_FACTOR的值是0.9,主要是容錯處理。

另外,這里的刪除算法,存在優化空間,目前Volley Map進行遍歷,然后依次刪除,可能這一秒我剛緩存的數據,下一秒就被刪除掉了,一些未過期的數據被刪除,已過期的數據還依然保留的情況。其實可以先刪除已過期的數據,在刪除最久未使用的數據(LRU)

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容