Just My Life & My Work

[iOS] 獲取WebView快取中的圖片

由於原生SDK沒有辦法直接獲取UIWebView中已下載的影像,因為想避免重複請求消耗資源避免減損使用者體驗,於是我找到可立馬使用的第三方套件:RNCachingURLProtocol。​雖然發現還要修改才能使用XD~

套件簡介

Simple offline caching for UIWebView and other NSURLConnection clients

WebView的圖片會使用NSURLProtocol來進行快取Cache,此第三方快取圖片會儲存到指定的文件夾,之後可以讀取此指定文件夾來獲取WebView的快取。

高人說套件重點如下:

  1. 儘早註冊你的URLProtocol(application:didFinishLaunchingWithOptions:)。
  2. NSURLProtocol是NSURLConnection的handler。 NSURLConnection的每個請求都會去遍歷所有的Protocols,並詢問你能處理這個請求麼(canInitWithRequest:)。如果這個Protocol返回YES,則第一個返回YES的Protocol會來處理這個connection。 Protocols的遍歷是反向的,也就是最後註冊的Protocol會被優先判斷。
  3. 當你的handler被選中了,connection就會調用–> initWithRequest:cachedResponse:client:,緊接著會調用–>startLoading。然後你需要負責回調:–> URLProtocol:didReceiveResponse:cacheStoragePolicy:,有些則會調用:–> URLProtocol:didLoadData: ,並且最終會調用–> URLProtocolDidFinishLoading:。你有沒有發現這些方法和NSURLConnection delegate的方法非常類似——這絕非偶然!
  4. 在online的情況下,RNCachingURLProtocol只是負責將請求轉發給一個新的NSURLConnection,並且拷貝一份結果給原來的connection。 在offline的時候, RNCachingURLProtocol就會從磁盤裡載入先前的結果,並將這些數據發回給連接。整個過程只有僅僅200行代碼(不包含Reachability)。
  5. 這裡還有一個有趣的問題,就是當RNCachingURLProtocol創建了一個新的NSURLConnection的,即新的connection也會去找一個handler。如果RNCachingURLProtocol說可以處理,那麼就死循環 (Dead Lock)了。怎麼解決呢?透過添加自定義HTTP Header(X-RNCache)來標記這個請求,告訴RNCachingURLProtocol不要再處理這個請求。
  6. 它可以響應所有的connection,所以你可能需要修改canInitWithRequest:來選擇你要緩存的數據。

我來修改

​在我看來,其實就是在不影響原本的流程,我寫個Class去參一腳連線過程,一旦碰到影像資源就把它Cache到我指定的資料夾(如上圖使用模擬器找出Cache位置),所以其實不是直接取用WebView的Cache喔~

原本我所參考的網頁,寫法跟套件有些出入,不過在我稍加修改後即可使用!

使用方法

註冊:開始Cache

[NSURLProtocol registerClass:[RNCachingURLProtocol class]];

反註冊:結束Cache

[NSURLProtocol unregisterClass:[RNCachingURLProtocol class]];

測試效果:

NSString *fileName = [RNCachingURLProtocol cachePathForURL:imageURL];
RNCachedData *memoryCache = [NSKeyedUnarchiver unarchiveObjectWithFile:fileName];
UIImage *image = [UIImage imageWithData:memoryCache.data];

客製化

還有讓我卡快一天的是設定支援的架構⋯⋯

[self setSupportedSchemes:[NSSet setWithObject:@"http"]];

因為我所載入的是加密過的網頁,所以改成這樣就能順利取得Cache!

[self setSupportedSchemes:[NSSet setWithObject:@"https"]];

作者說明使用方法其實可以更彈性,不用一開始就註冊要去Cache網路資源,需要的時候再去註冊即可!

這個套件的特性如作者說明:

RNCachingURLProtocol is a simple shim for the HTTP protocol (that’s not nearly as scary as it sounds). Anytime a URL is downloaded, the response is cached to disk. Anytime a URL is requested, if we’re online then things proceed normally. If we’re offline, then we retrieve the cached version.

像是我專案中請求網路資源的Link有來自API,也有來自Web,現在我只想要UIWebView中的影像Link,就特別在該畫面做註冊與反註冊,如此節約效能。我與資深工程師協同合作,他發現可能會與SDWebImage套件衝突,也就是重複Cache,所以我們特別在UIWebView才使用RNCachingURLProtocol。

測試時可以在WebView的delegate,shouldStartLoadWithRequest中取得Request(如上程式碼),先查之前有無Cache,若有的話就不要再下載,直接取用本機的Cache。

話說這套件已年久失修,主要的檔案最後修改是在2014年,不過好在只有一個Class,我稍加修改.m和.h檔案,就能隨心所欲使用囉!

若有需要的話,要自己實作清理本機端的Cache喔~

參考:使用NSURLProtocol實現本地靜態文件的web服務器使用NSURLProtocol實現UIWebView的離線緩存iOS UIWebView 圖片內存緩存交互二維碼識別

廣告

隨意留個言吧:)~

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

標籤雲

%d 位部落客按了讚: