? ? ? 本文主要從源碼的角度分析 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)