WkWebView攔截替換本地音訊,圖片

Crazy巴旦木發表於2018-08-08

需求

需求是要把網頁快取到本地,以防使用者無訊號使用時無法使用。

執行

那就開始做吧,以往做的只是簡單的App和H5的互動,傳傳資料啊,獲取獲取事件並作出響應啊之類的,現在讓做快取整個網頁沒思路。就先百度了,果不其然有了解決方案,感謝以下大神給的啟發:webView離線快取機制,這裡解釋了好幾種方案,但都不可實施,但這裡學到了很多關於webView快取的知識和方案,有的在UIWebView上是可以的,但是在WKWebView上不可以。
後來又查到了這篇文章WKWebView的快取,我們在前面看到WKWebView雖然走canInitWithRequest方法,但是後面的整個流程就與NSURLProtocol無關了,大神的思路來源是iOS WKWebView (NSURLProtocol)攔截js、css,圖片資源,找到了使用WKBrowsingContextController和registerSchemeForCustomProtocol,通過反射拿到私有class/selector,通過kvc取到browsingContextController,通過把註冊http和https請求交給NSURLProtocol處理。
[NSURLProtocol wk_registerScheme:@"http"];
[NSURLProtocol wk_registerScheme:@"https"];
看到這裡就可以實現WKWebView的快取了,捋一下思路,通過kvc的方式拿到私有介面browsingContextController,在呼叫registerSchemeForCustomProtocol的時候把http和https也加進去,這樣就能攔截到http和https的請求了,然後自己寫繼承NSURLProtocol的自定義類,重寫canInitWithRequest,startLoading等方法,在canInitWithRequest裡就可以攔截到http(s)請求了,需要判斷是否已經攔截處理過,否則會一直迴圈呼叫的,在startLoading中檢測本地快取資料,如果有的話就使用並且開啟非同步執行緒去更新快取,如果沒有就去下載呼叫NSURLSession快取到NSURLCache中,至此就完成了。

新問題

又發現了新的問題,網頁剛開啟的時候是可以快取看到的資料的,但是當點選播放一段音訊或者顯示一張圖片時,就會發現這部分資料未快取下來。由於這部分屬於h5中的點選之後才能請求下來的,也就是說使用者必須把所有的點選事件點選一遍才能把需要的圖片或音訊按照上面的方式快取到本地,萬一使用者進來什麼也沒點,就出去,沒網了再開啟網頁發現只能看,點選之後的音訊或圖片都不能用,這達不到我們的需求。

下面是我自己完成後的思路

既然WkWebView不能自動快取所有需要的資料,那快取不了的資料就讓我們自己來下載吧,但是如何把本地資料放入網頁中呢。
安卓小夥伴先實現了這樣的功能,並從他那邊得到了思路,android webview Not allowed to load local resource錯誤的解決辦法,他的實現思路是替換本地路徑給到網頁,但是發現網頁掉本地路徑會出現not allowed to load local,文章的大神給出了攔截URL替換資源的方法,我在想蘋果是不是也可以呢?但是蘋果沒有類似安卓 shouldInterceptRequest的方法,安卓的思路是如果請求包含約定的欄位 說明是要拿本地的資料,我們在這裡重新構造WebResourceResponse 將資料已流的方式傳入。
剛開始想我在webView Finish 中用document.image[0] 替換本地流的方式,倒是替換了,但是這樣的話不能替換我所要替換的圖片啊,不能圖片都是下標 0 1 2 3的替換吧,到底在哪兒寫替換呢,我又如何獲取到需要替換圖片的路徑,然後把資源給它呢,也不能獲取到js程式碼,在裡面操作吧?
那天整理WKWebView的快取機制流程,發現其實NSURLProtocol也有相應的方法,就是在startloading中,也是拿到快取資料流並去網頁更新快取的,如果我在這裡判斷攔截到的路徑是圖片的話,我就可以把本地的資料流以原來從NSURLCache拿到資料流更新網頁的方式來替換網頁資料了。試了一下果然可以,既然圖片轉成NSData資料流的方式可以,那麼音訊應該也可以,果不其然完美實現了資料流的替換,這樣就實現了網頁所有資料的快取。只不過一部分是網頁自動快取好的,一部分它自己沒有自動快取的,我們拿自己下載好的本地資料流。

相關文章