1. 程式人生 > >android Volley原始碼解析筆記

android Volley原始碼解析筆記

現在android主流用的網路請求框架是Okhttp3和Retrofit2+Rxjava兩種,而我之前很多專案都是用的Volley,然後閱讀開源框架原始碼的經驗不夠,所以就拿Volley練手了。
本文不會貼太多原始碼,我是帶著問題去分析volley原始碼,如果想邊看原始碼邊解析的,可以看下這位童鞋的一系列文章

Volley對於https的支援

Volley本身支援https,但是由於HurlStack中171行的mSslSocketFactory沒有被例項化,導致volley預設不支援https,通過新增實現了X509TrustManager的HTTPSTrustManager類,從而使mSslSocketFactory被例項化,volley就能支援信任所有https啦。

volley不適合post大量資料,也不適合上傳下載大量檔案原因

  1. volley中為了提高請求處理的速度,採用了ByteArrayPool進行記憶體中的資料儲存的,如果下載大量的資料,這個儲存空間就會溢位;

  2. volley的執行緒池(4個工作執行緒+1個阻塞佇列)是基於陣列實現的,不會自動擴充套件,一旦大資料上傳或者下載長時間佔用了執行緒資源,後續所有的請求都會被阻塞。

volley適用情況?

volley儲存的時候優是從ByteArrayPool中取出一塊已經分配的記憶體區域, 不必每次存資料都要進行記憶體分配,而是先查詢緩衝池中有無適合的記憶體區域,如果有,直接拿來用,從而減少記憶體分配的次數 ,所以他比較適合頻次高但資料量少的網路資料互動情況。

volley中關於磁碟快取

首先,磁碟快取是針對於靜態檔案的,典型的就是圖片檔案了,如果用了volley的ImageLoader去下載圖片,就會用到磁碟快取(這裡我糾結了好久,我之前還一直以為普通網路請求也可以用到磁碟快取的)。關於磁碟快取,主要是在DiskBasedCache類中實現。volley快取有條件的,一個是手動設定要不要快取,一個是看伺服器返回的和快取相關的頭資訊是不是空,得伺服器支援才行。當用ImageLoader下載了圖片時,會在/sdcard/Android/data/packageName/cache/volley目錄下生成很多個檔名為一串數字的檔案,命名規則為

private String getFilenameForKey(String key) {
int firstHalfLength = key.length
() / 2; String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode()); localFilename += String.valueOf(key.substring(firstHalfLength).hashCode()); return localFilename; }

關於http快取

如果你返回的json資料頭資訊也設定了快取,會快取的,但是一般伺服器不會設定快取這些資料的
http快取分為強制快取和比較快取兩種快取機制:
a. 對於強制快取,伺服器通知瀏覽器一個快取時間,在快取時間內,下次請求,直接用快取,不在時間內,執行比較快取策略。
b. 對於比較快取,將快取資訊中的Etag和Last-Modified通過請求傳送給伺服器,由伺服器校驗,返回304狀態碼時,瀏覽器直接使用快取。
快取只針對於靜態檔案,主要是針對web瀏覽器上的,volley只是多做了一步,其實沒啥用。

volley如何將靜態檔案寫入到檔案中的?

在DiskBasedCache類的put()方法中,將從伺服器中獲取的Entry物件中的data資料寫入到檔案輸出流中,檔案命名規則是getFilenameForKey()

網路執行緒排程(NetworkDispatcher) vs 快取執行緒排程(CacheDispatcher)

Volley初始化以後就建立了5個後臺執行緒在處理請求。只要你沒做處理,這5個執行緒一直在後臺跑。為了節省資源,在同一個App中最好使用同一個單例Volley RequestQueue佇列來處理所有請求,以免建立過多執行緒浪費資源。還有在退出這個應用時,應該呼叫 RequestQueue#stop方法來幹掉所有Volley執行緒。如此才是使用Volley最優雅的方式
預設4個網路執行緒排程,1個快取執行緒排程。快取執行緒排程是用於靜態檔案的,常見的就是圖片下載。NetworkDispatcher和CacheDispatcher的run()方法中,都有阻塞佇列類BlockingQueue,都用了take()方法阻塞著,如果一個UI中有大於4個網路請求,最多隻有4個工作執行緒,其他網路請求只得等這4個工作執行緒中有操作完了的,才能用此執行緒。

阻塞佇列 vs 執行緒池

阻塞佇列(BlockingQueue)和執行緒池(ExecutorsService)是兩個不同的概念。比如說在Volley中的網路請求執行緒排程,預設生成4個網路請求執行緒,每個執行緒的run()方法都會有同一個阻塞佇列物件在那阻塞著,相當於一個阻塞佇列物件對應4個網路請求工作執行緒,看哪個執行緒工作完,就執行阻塞等待。而執行緒池,我發現Volley網路請求時,並沒有用到執行緒池,只是用了阻塞佇列和預設的4個工作執行緒。而在網路響應分發也沒用執行緒池,只用了Executor介面而已。 所以,Volley壓根就沒用執行緒池。其實就是用4個工作執行緒加上1個阻塞佇列,充當了執行緒池的角色。app端不管一個頁面有多少個請求,也還是得有執行緒池的思想,否則當頁面多時,new出來的執行緒越來越多,這肯定是不合理的。

執行緒分發機制(主要用於返回的資料)

在RequestQueue的構造方法中new了一個ExecutorDelivery例項,用於從伺服器返回的資料分發。

StringRequest request = new StringRequest(Request.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {

                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {

                }
            });

ExecutorDelivery中內部類ResponseDeliveryRunnable裡呼叫了mRequest.deliverResponse()方法,而StringRequest類的deliverResponse()方法有呼叫了內部類Listener的onResponse()方法,從而將伺服器請求的資料,以string的形式吐了出來。