webView 離線緩存機制

一:需求背景

1、應用場景

App中存在大量通過Web實現的業務,但是產品開發中沒有對應web緩存機制,導致用戶在使用產品的時候會重複拉取一些公共資源,這些公共資源包括CSS,JS,圖片。因此希望設計一套Webview緩存機制,解決App對公共資源的離線加載以及更新機制。

2、Webview緩存 帶來以下三個優勢:

速度 – 緩存資源爲本地資源,因此加載速度較快,提供用戶體驗。服務器負載更少 – 瀏覽器只會從發生了更改的服務器下載資源。離線瀏覽 – 用戶可在離線時瀏覽您的完整網頁。

3、離線本地存儲和傳統的瀏覽器緩存有什麼不同呢?離線存儲爲整個web提供服務,瀏覽器緩存只緩存單個頁面;
離 線存儲可以指定需要緩存的文件和哪些文件只能在線瀏覽,瀏覽器緩存無法指定;離 線存儲可以動態通知用戶進行更新。


二:預研方案1、通過註冊自定義NSURLCache實現webview 離線緩存


通過用自定義NSURLCache的子類CustomURLCache,重寫對request的cacheresponse相應方法,實現request對server請求之前進行攔截。然後把request作爲key值在URLCache中查詢是否緩存有對應的response,如果有,則直接返回不再需要進行請求;如果沒有,那麼返回空值讓web進行請求,同時開啓一個請求任務對request進行下載,下載成功後把數據和request作爲value和key緩存到URLCache。

以上方案能給ios中的UIWebView實現了Web離線緩存機制。但是目前我們產品業務中對於webview存在兩種場景。ios8.0之前系統使用UIWebView,ios8.0+的系統使用了WKWebview。而WKWebview是一個獨立的進程相對於App,使用該方案並不能攔截WKWebview的URLCache。因此通過自定義NSURLCache來實現Web離線緩存機制,不能同時滿足UIWebView和WKWebview,也就不能滿足我們產品業務。




2:通過註冊自定義NSURLProtocol

既然自定義URLCache不能完美滿足Web離線緩存機制,那麼我們又研究了Apple 的URLloading System,然後想辦法通過自定義URLProtocol來攔截URL Request。在研究過程發現自定義URLProtocol對URL 進行攔截後還是存在問題,問題就是通過WKWebview發起的Post請求,HttpBody會被置空。WKWebview 置空 HttpBody是Appe 爲了進程間通信的性能問題而設計的。如下webKit源碼所示:

// We don't send HTTP body over IPC for better performance.

// Also,it's not always possible to do,as streams can only be created in process thatdoes networking.

RetainPtr<CFDataRef> requestHTTPBody =adoptCF(CFURLRequestCopyHTTPRequestBody(requestToSerialize.get()));

RetainPtr<CFReadStreamRef> requestHTTPBodyStream =adoptCF(CFURLRequestCopyHTTPRequestBodyStream(requestToSerialize.get()));

if(requestHTTPBody || requestHTTPBodyStream){

CFMutableURLRequestRef mutableRequest = CFURLRequestCreateMutableCopy(0,requestToSerialize.get());

requestToSerialize = adoptCF(mutableRequest);CFURLRequestSetHTTPRequestBody(mutableRequest,nil);CFURLRequestSetHTTPRequestBodyStream(mutableRequest,nil);

}

因此該方案也不能解決產品對web離線緩存機制的需求。

ps:蘋果工程師在針對WKWebview丟失HttpBody做出答覆的同時,他們對於開發者想通過WKWebview實現Web 離線緩存機制提出另外的一種方案,即WKWebview只負責html的展示,而交互 通過jsBridge,web 請求則通過native 代理請求。native請求回來html則塞給WKWebview顯示,這種模式基本就相當於推倒我們目前業務代碼重開發一樣,因此這裏不建議也不繼續深入研 究 該方案。


3: 通過設置HTML的manifest文件


在以上兩種方案都不能實現web離線緩存後,我查看了Apple 對於 Safari Client-SideStorage And Offline Application Proframming Guide中說可以通過web Server 設置manifest文件來實現離線緩存機制。

W3C官方對manifest的介紹是HTML5 引入了應用程序緩存,這意味着 web 應用可進行緩存,並可在沒有因特網連接時進行訪問。站點中的其他頁面即使沒有設置manifest屬性,請求的資源如果在緩存中也從緩存中訪問。

manifest文件加載原理過程圖:

manifest文件結構主要分爲三個部分:
CACHE MANIFEST - 在此標題下列出的文件將在首次下載後進行緩存NETWORK - 在此標題下列出的文件需要與服務器連接,且不會被緩存FALLBACK - 在此標題列出的文件規定當頁面無法訪問d的時候回退頁面

manifest更新緩存機制,一旦應用被緩存,它將會保持緩存直到發生以下情況:manifest文件被修改
用js調用應用緩存對應方法

manifest的使用:

只需要對webServerd的mime types設置爲text/cache-manifest,然後在html頁面引入manifest文件即可。

自己在調研過程中,通過manifest的確可以實現web緩存機制。但是在測試的過程以及網上了解manifest信息,發現manifest有以下缺點:

1: w3c已經不再維護manifest,也不再把manifest作爲標準。2:manifest文件修改後,cache必須全部更新,不能指定具體某個資源更新。3:如果manifest文件,或者內部列舉的某一個文件不能正常下載,整個更新過程都將失敗,瀏覽器繼續全部使用老的緩存。4:引用manifest的html必須與manifest文件同源,在同一個域下。5:系統會自動緩存引用清單文件的HTML文件。





<!DOCTYPE HTML>
<html manifest="demo.appcache">...
</html>

6:對於緩存了的html文件,如果html頁面中存在某些資源沒有在manifest中進行緩存,也沒有把這些資源設置爲NETWORK,那麼該頁面在再次請求顯示的時候,這些資源將無法展示。以下URL是測試用例(http://appcache-demo.s3-website-us-east-1.amazonaws.com/without-network/)通過打開該鏈接,然後關閉網頁,最後再次打開該網頁,那麼網頁中的圖片將不能加載顯示。

基於自己的測試結果以及網上獲取的信息,manifest進行web緩存機制也不滿足我們產品業務。

三:調研結論

目前我們項目中對於web業務實現用了UIWebView以及WKWebview,如果用戶系統是iOS8之前系統則使用UIWebView,iOS8以及iOS8+系統則使用WKWebview。對於UIWebView是可以實現Webview緩存機制,但WKWebview目前沒有找到適用的Webview緩存機制,這個也是因爲WKWebview在App加載Web業務的時候,它其實是一個獨立進程。Apple 對於WKWebview發起的request會把HttpBody置空,這是Apple爲了保障進程間通信性能而設計的。

我們也不建議爲了Webview緩存在項目中使用UIWebView替換WKWebView。因爲Apple已經不再對UIWebView維護了,而且UIWebView在性能,內存,或者穩定性上都比WKWebview差。因此我們只能保持持續的對Apple iOS系統更新進行關注,關注Apple對於WKWebview的升級是否滿足Web緩存機制。在關注的同時也在尋找合理方案爲實現產品動態化開發且能保持優秀的用戶體驗。

參考文獻:
1:STMURLCache實現UIWebView的緩存機制&web預加載https://github.com/ming1016/STMURLCache
2:Apple工程師對WKWebview 丟失request body的回覆
https://forums.developer.apple.com/message/59306#59306
3:Stackoverflow 對WKWebview post 丟失bodyd的討論https://stackoverflow.com/questions/26253133/cant-set-headers-on-my-wkwebview-post-request/26342224#263422244:蘋果對safari瀏覽器離線緩存機制引導說明(通過manifest實現離線緩存)https://developer.apple.com/library/content/documentation/iPhone/Conceptual/SafariJSDatabaseGuide/OfflineApplicationCache/OfflineApplicationCache.html#//apple_ref/doc/uid/TP40007256-CH7-SW1
5:UIWebview 與 WKWebview 比較
http://www.cnblogs.com/junhuawang/p/5759224.html
6:HTML5 離線緩存mainifest淺析
http://blog.csdn.net/szs860806/article/details/70171772 

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