天天看點

WebView 緩存原理分析和應用

一、背景

現在的app開發,或多或少都會用到hybrid模式,到了webview這邊,經常會加載一些js檔案(例如和webview用來native通信的bridge.js),而這些js檔案不會經常發生變化,是以我們希望js在webview裡面加載一次之後,如果js沒有發生變化,下次就不用再發起網絡請求去加載,進而減少流量和資源的占用。那麼有什麼方式可以達到這個目的呢?先得從webview的緩存原理入手。

二、webview的緩存類型

webview主要包括兩類緩存,一類是浏覽器自帶的網頁資料緩存,這是所有的浏覽器都支援的、由http協定定義的緩存;另一類是h5緩存,這是由web頁面的開發者設定的,h5緩存主要包括了app cache、dom storage、local storage、web sql database 存儲機制等,這裡我們主要介紹app cache來緩存js檔案。

三、浏覽器自帶的網頁資料緩存

1.工作原理

浏覽器緩存機制是通過http協定header裡的cache-control(或expires)和last-modified(或 etag)等字段來控制檔案緩存的機制。關于這幾個字段的作用和浏覽器的緩存更新機制,大家可以看看這兩篇文章(h5 緩存機制淺析 移動端 web 加載性能優化,android:手把手教你建構 webview 的緩存機制 & 資源預加載方案),裡面有詳細的介紹。下面從我實際應用的角度,介紹一下通常會在http協定中遇到的header。

這兩個字段是接收響應時,浏覽器決定檔案是否需要被緩存;或者需要加載檔案時,浏覽器決定是否需要送出請求的字段。

cache-control:max-age=315360000,這表示緩存時長為315360000秒。如果315360000秒内需要再次請求這個檔案,那麼浏覽器不會送出請求,直接使用本地的緩存的檔案。這是http/1.1标準中的字段。

expires: thu, 31 dec 2037 23:55:55 gmt,這表示這個檔案的過期時間是2037年12月31日晚上23點55分55秒,在這個時間之前浏覽器都不會再次送出請求去擷取這個檔案。這是http/1.0中的字段,如果用戶端和伺服器時間不同步會導緻緩存出現問題,是以才有了上面的cache-control,當它們同時出現在http response的header中時,cache-control優先級更高。

下面兩個字段是發起請求時,伺服器決定檔案是否需要更新的字段。

last-modified:wed, 28 sep 2016 09:24:35 gmt,這表示這個檔案最後的修改時間是2016年9月28日9點24分35秒。這個字段對于浏覽器來說,會在下次請求的時候,作為request header的if-modified-since字段帶上。例如浏覽器緩存的檔案已經超過了cache-control(或者expires),那麼需要加載這個檔案時,就會送出請求,請求的header有一個字段為if-modified-since:wed, 28 sep 2016 09:24:35 gmt,伺服器接收到請求後,會把檔案的last-modified時間和這個時間對比,如果時間沒變,那麼浏覽器将傳回304 not modified給浏覽器,且content-length肯定是0個位元組。如果時間有變化,那麼伺服器會傳回200 ok,并傳回相應的内容給浏覽器。

etag:”57eb8c5c-129”,這是檔案的特征串。功能同上面的last-modified是一樣的。隻是在浏覽器下次請求時,etag是作為request header中的if-none-match:"57eb8c5c-129"字段傳到伺服器。伺服器和最新的檔案特征串對比,如果相同那麼傳回304 not modified,不同則傳回200 ok。當etag和last-modified同時出現時,任何一個字段隻要生效了,就認為檔案是沒有更新的。

2.webview如何設定才能支援上面的協定

由上面的介紹可知,隻要是個主流的、合格的浏覽器,都應該能夠支援http協定層面的這幾個字段。這不是我們開發者可以修改的,也不是我們應該修改的配置。在android上,我們的webview也支援這幾個字段。但是我們可以通過代碼去設定webview的cache mode,而使得協定生效或者無效。webview有下面幾個cache mode:

load_cache_only: 不使用網絡,隻讀取本地緩存資料。

load_default: 根據cache-control決定是否從網絡上取資料。

load_cache_normal: api level 17中已經廢棄,從api level 11開始作用同load_default模式

load_no_cache: 不使用緩存,隻從網絡擷取資料。

load_cache_else_network,隻要本地有,無論是否過期,或者no-cache,都使用緩存中的資料。本地沒有緩存時才從網絡上擷取。

設定webview緩存的cache mode示例代碼如下:

websettings settings = webview.getsettings();

settings.setcachemode(websettings.load_default);

網上很多人都說根據網絡條件去選擇cache mode,當有網絡時,設定為load_default,當沒有網絡時設定為load_cache_else_network。但是在我的業務中,js檔案的更新都是非覆寫式的更新,也就是時候每次改變js檔案的時候,檔案的url位址一定會發生變化,是以我希望浏覽器能夠緩存下來js,并且一直使用它,那麼我就給它隻設定為load_cache_else_network。當然如果你要是可以改js的cdn伺服器的cache-control字段,那也行啊,用load_default就ok了。至于檔案是應該采用覆寫式or非覆寫式的更新,不是我今天要讨論的内容,在web前端領域,這是一個可以聊聊的topic。

關于ios的webview,我同僚在實際測試的時候竟然發現,控制檔案緩存的response header是expires字段。。而且ios無法針對整個webview設定cache mode,隻能針對每一個urlrequest去設定。。後續有機會要學習一下ios那塊的情況。

3.在手機裡面的存儲路徑

浏覽器預設緩存下來的檔案是怎麼被存儲到了哪裡呢?這個問題在接觸到webview以來,就一直是一個謎題。這次由于工作的需要,我特意root了兩台手機,一台紅米1(android 4.4)和一台小米4c(android 5.1),在root高系統版本(6.0和7.1)的兩台nexus都以失敗告終之後,我決定還是先看看4.4和5.1系統上,webview自帶的緩存存到了哪裡。

首先,不用思考就知道,這些檔案一定是在/data/data/包名/目錄下,在我之前的一篇部落格裡面提到過,這是每一個應用自己的内部存儲目錄。

接着,我們打開終端,使用adb連接配接手機,然後按照下面指令操作一下。

// 1.先進入shell

adb shell

// 2.開啟root賬号

su

// 3.修改檔案夾權限

chmod 777 data/data/你的應用包名/

// 4.修改子檔案夾的權限,因為android指令行不支援向linux那樣的-r指令實作遞歸式的chmod。。。

chmod 777 data/data/你的應用包名/*

// 5.是以如果你對應用目錄層級更深,你就要進一步地chmod。。。

chmod 777 data/data/你的應用包名//

// 6.直到終端裡提示你說,no such file or directory時,說明chmod完了,所有的内部存儲裡面的檔案夾和檔案都可以看到了,如果大家有更好的方法請一定告訴我,多謝了~

android 4.4的目錄:/data/data/包名/app_webview/cache/,如下圖所示的第二個檔案夾。

可能你注意到了,第一個檔案夾是叫application cache,我們後面再說它。

android 5.1的目錄:/data/data/包名/cache/org.chromium.android_webview/下面,如下圖所示。

但是在5.1系統上,/data/data/包名/app_webview/檔案夾依然存在,隻是4.4系統上面存儲webview自帶緩存的app_webview/cache檔案夾不再存在了(注意下app cache目錄還在),如下圖所示。

綜上所述,webview自帶的浏覽器協定支援的緩存,在不同的系統版本上,位置是不一樣的。也許除了我root過的4.4、5.1以外,其他版本系統的webview自帶緩存還可能存在于不同的目錄裡面。

另外一個是關于緩存檔案的存儲格式和索引格式,在不同的手機上可能也有差别,因為之前看到網上的人都說有叫webview.db或者webviewcache.db的檔案,這個檔案呢,還不是在app_webview/cache或者org.chromium.android_webview下面,而是在/data/data/包名/database/裡面。但是,我這兩台root過的手機都沒有看到這種檔案,而且我把/data/data/包名/下面所有的db檔案都打開看了,并沒有發現有存儲url記錄的table。。

實際上,以5.1系統為例,我看到了/data/data/包名/cache/org.chromium.android_webview/下面有叫index和/index-dir/the-real-index的檔案,以及一堆名稱為md5+下劃線+數字的檔案,上面的圖中也可以看得到,這塊的原理仍然有些疑問,也希望專業的大神可以解答一下。

四、h5的緩存

講完了webview自帶的緩存,下面講一下h5裡面的app cache。這個cache是由開發web頁面的開發者控制的,而不是由native去控制的,但是native裡面的webview也需要我們做一下設定才能支援h5的這個特性。

寫web頁面代碼時,指定manifest屬性即可讓頁面使用app cache。通常html頁面代碼會這麼寫:

manifest="xxx.appcache">

繼續閱讀