Volley源碼閱讀

從Volley的使用步驟來看源碼,首先是new一個RequestQueue,來看Volley.java的newRequestQueue方法:

通過重載方法的調用最終會調用執行這個方法:

在這個方法裏首先初始化cacheDir,然後根據API版本是否大於9初始化一個HurlStack對象或是一個HttpClientStack對象。緊接着初始化一個BasicNetwork對象和一個RequestQueue對象,並且調用了RequestQueue的start方法。一下子多這麼多對象,一個個來看看這些都是幹些啥的?首先是HurlStack查看源碼發現HurlStack繼承HttpStack接口,而HttpStack接口只有一個performRequest方法。

返回來看HurlStack類:


查看源碼大概能看出來HurlStack類的作用通過HttpURLConnection處理Http請求並返回結果。HttpClientStack對象同樣是通過HttpClient處理Http請求。接着將stack傳入BasicNetwork對象中,在BasicNetwork中調用HttpStack.performRequest的方法得到了一個httpResponse,在封裝成NetworkResponse類返回。

最後RequestQueue這個類顧名思義是一個請求調度隊列,來看他的start方法。


start方法中又new 了一個CacheDispatcher,CacheDispatcher這個看名字是一個緩存調度線程,緊接着循環new出多個(默認四個)NetworkDispatcher網絡請求調度線程,並且調用了線程的start方法。這樣一共就有五個線程在運行等待請求進入。
按照Volley的使用流程,創建完請求隊列後就是創建各種Request請求通過調用RequestQueue請求隊列的add方法,將Request加入隊列。所以接着看RequestQueue的add方法。

add方法中238行首先是判斷傳進來的Request是否允許緩存,如果不允許就直接加入網絡請求隊列mNetworkQueue。允許再判斷等待請求隊列中是否已經有了相同的請求如果有就加入到mWaitingRequests隊列中,沒有就加入緩存隊列mCacheQueue中。到此add方法結束,add方法中並沒有請求網絡或操作緩存。當將請求加入到緩存線程或者網絡請求線程中時,在之前RequestQueue中start初始化緩存調度線程CacheDispatcher和網絡請求調度線程NetworkDispatcher,就會輪詢各自的隊列,發現請求任務就開始執行。

接下來首先CacheDispatcher的run方法裏的代碼:



while(true)首先看到的是這個,就說明這是個一直執行的線程。mCacheQueue.take()先從緩存隊裏取出一個請求,判斷請求是否被取消,沒被取消再判斷緩存中的響應結果是否爲null,如果爲null就把請求加入網絡請求隊列,如果沒有再判斷響應是否過期,過期了同樣再把請求加入網絡請求隊列,沒過期調用request.parseNetworkResponse方法解析Response。接着再判斷返回響應是否需要refresh,不需要就調用ResponseDelivery的postResponse方法將請求和響應回調到主線程,如果需要同樣就調用ResponseDelivery的postResponse方法將請求和響應回調到主線程但是同時還將該請求加入到網絡請求隊列中刷新響應。

接着查看NetworkDispatcher網絡請求調度線程的run方法

同樣的while(true)一直循環執行,從隊列中取出一個請求判斷是否被取消,沒被取消調用Network的performRequest方法傳入request返回一個NetworkRespose結果。這裏的Network就是最初在newRequestQueue中初始化的BasicNetwork,BasicNetwork的performRequest方法中再次調用了 mHttpStack.performRequest,這裏的mHttpStack同樣是之前在newRequestQueue方法中根據API等級創建的HurlStack或HttpClientStack對象,分別是使用HttpURLConnection和HttpClient來發送完成網絡請求。最終會封裝成一個NetworkRespose結果對象返回。

接下來拿到返回結果後125行調用request.parseNetworkResponse解析返回的數據,並且根據是否緩存寫入緩存,最後和CacheDispatcher同樣調用ResponseDelivery的postResponse方法將請求和響應回調到主線程。在這裏我們看到解析數據是調用的Request類的方法,是因爲不同Request解析方法不一樣,這就是我們自定義Request時候必須重寫parseNetworkResponse方法的原因。
接下來來看ResponseDelivery這個接口具體是怎樣將響應返回到主線程的:

NetworkDispatcher裏的ResponseDelivery由構造函數初始化傳入,最終在RequestQueue的構造函數裏找到:

這裏new了一個ExecutorDelivery傳入的是主線程的Handler,繼續查看ExecutorDelivery

ExecutorDelivery的構造函數中初始化一個mResponsePoster,在execute方法中通過傳入的主線程的handler處理Runnable,就可以在主線程執行了。接着來看postResponse方法:

調用mResponsePoster.execute傳入了一個ResponseDeliveryRunnable對象,繼續深入ResponseDeliveryRunnable是一個私有內部類:

可以看到run方法裏在判斷請求響應是否成功後分別調用了 mRequest.deliverResponse方法和 mRequest.deliverError方法。這兩個方法又是Request的方法,deliverError在Request類中返回的是一個VolleyError類型,而deliverResponse在Request類中是一個抽象方法,在不同的Request中實現不同,例如StringRequest:

這個mListener就是在創建StringRequest時候傳入的Listener,執行請求成功後的操作。同理ImageRequest中返回的就是個Bitmap:

至此 Volley的使用流程也就走通了,整個流程的源碼也就看完一遍,瞭解了Voelly中從請求隊列創建再往隊列里加入Request請求,最終網絡請求執行完成回調到主線程的過程。整體的流程圖如下:

那麼爲什麼說適合數據量小,通信頻繁的網絡操作呢?數據量大的不適合不能做嗎?
答案還是要從源碼中尋找,找到網絡請求調度線程,從這裏來看:

在NetworkDispatcher裏調用了Network的performRequest方法來獲得請求結果,Network接口的實現類是BasicNetwork,進入BasicNetwork裏來看performRequest方法:

這裏之前看過實際上是調用了HttpStack.performRequest去獲得httpResponse。在得到httpResponse之後又判斷了httpResponse中的實體Entity是否爲null,不爲null調用了一個entityToBytes方法,從名字可以看出這應該是把實體Entity轉成byte[]數組的方法,最終將byte數組傳入NetworkResponse對象返回。

再深入entityToBytes方法查看:

從這個方法可以看出這就是從entity中讀出content到bytes裏,就是個輸入輸出流的讀寫過程,通過toByteArray方法返回一個byte[]數組。和一般不同的是read所用的byte[]不是直接new出來的是通過 mPool.getBuf(1024)獲得的,也就是說不用每次從內存新分配一塊區域,從這個Pool緩存池取就可以了,那麼來看這個Pool是個啥?

從74行可以看出這個緩存池的大小是4096字節,具體還要看ByteArrayPool這個類

ByteArrayPool中有兩個byte[]的List,mBuffersByLastUse是按byte數組使用時間先後排序存放,mBuffersBySize是按byte數組大小長度來存放。

ByteArrayPool主要就這三個方法,getBuf()從mBuffersBySize取出一個適合大小的byte[],從代碼來看循環mBuffersBySize找到長度大於所需長度的數組就返回這個byte[],並且從mBuffersBySize和mBuffersByLastUse中移除這個byte[],當前Pool長度也減去該byte[]的長度,如果過沒有找到就new一個新的適合長度的byte[]返回。returnBuf()方法是當取出的byte[]使用結束後返回到Pool中,從代碼看首先把這個剛用完的byte[]添加到mBuffersByLastUse最後,再通過二分查找法找到byte[]長度適合的在mBuffersBySize裏的位置,將byte[]放入mBuffersBySize,並且將當前Pool長度加上該byte[]的長度,然後調用清理方法trim(),判斷當前Pool長度是否超過最大長度,超過了的話就從mBuffersByLastUse移除第0個byte[],並且同時從mBuffersBySize移除這個byte[],當前Pool長度也減去相應的長度。
回過頭來,再回到爲什麼說Volley適合數據量小,通信頻繁的網絡操作的問題上,首先可以看出每次請求返回結果都需要保存在內存中,並且需要通過ByteArrayPool轉換,但是ByteArrayPool長度默認只有4096字節,所以返回結果數據量太大會造成溢出。其次也是因爲使用ByteArrayPool不用每次都重新分配新的內存空間,而是先從ByteArrayPool中尋找是否有合適的byte[],減少了內存分配的次數,所以說Volley適合數據量小,通信頻繁的網絡網絡請求的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章