我是如何一步一步實現網頁離線緩存的?

来源:http://www.cnblogs.com/zhanggui/archive/2017/11/28/7909355.html
-Advertisement-
Play Games

一個Hybrid APP,如何做離線緩存策略?也可以簡單來說,你的APP只是一個殼,裡面真正載入的內容是H5,如果優化載入內容的速度? ...


問題

一個Hybrid APP,如何做離線緩存策略?也可以簡單來說,你的APP只是一個殼,裡面真正載入的內容是H5,如果優化載入內容的速度?

先瞭解一下NSURLProtocol

從字面意思看它是一個協議,但是它其實是一個類,而且繼承自NSObject。它的作用是處理特定URL協議的載入。它本身是一個抽象類,提供了使用特性URL方案處理URL的基礎結構。你可以自己創建NSURLProtocol的子類,來讓自己的應用支持自定義的協議或者URL方案。
應用程式永遠不需要直接實例化一個NSURLProtocol子類。當一個下載開始的時候,系統創建一個合適的protoco對象來響應URL請求。你要做的就是自己定義一個你自己的protocol,然後在APP啟動的時候調用registerClass:,讓系統知道你的協議。
這裡需要註意:你不能在watchOS 2以及更高版本中自定義URL scheme和協議。

為了支持特定的自定義請求,你最好定義NSURLRequest 或者NSMutableURLRequest。讓自定義的這些對象來實現請求,這裡需要使用NSURLProtocol的propertyForKey:inRequest:和setProperty:forKey:inRequest,然後你可以自定義NSURLResponse類來模擬返回信息。
接下來就開始對UIWebView進行離線緩存處理。

UIWebView的離線緩存處理

首先,我們需要自定義一個NSURLProtocol的子類,並且在AppDelegate.m的

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSURLProtocol registerClass:[ZGURLProtocol class]];
    return YES;
}

註冊。接下來的所有操作就都是在我們自定義的ZGURLProtocol中操作了。先看一下registerClass的作用:
嘗試註冊一個NSURLProtocol的子類,使其對URL Loading System可見。這裡的URL Loading System就是一組類和協議,允許你的應用程式訪問由URL產生的內容,比如請求、接收內容和Cache等。當URL Load System開始載入一個請求的時候,每個註冊的協議類都被依次去調用,以確定是否可以用指定的請求去初始化它。首先被調用的方法是:

+ (BOOL)canInitWithRequest:(NSURLRequest *)request;

在該方法裡面進行緩存過濾,比如你想只緩存js,那麼判斷request的path的尾碼,如果是js,就返回YES,否則返回NO。
如果返回YES,那麼就相當於該請求被自定義的URLProtocol來處理,這裡不能保證所有的註冊的NSURLProtocol都能被處理到。如果你定義了多個NSProtocol子類,這些子類將會以相反的順序調用。也就是說如果你是這樣寫的:

 [NSURLProtocol registerClass:[ZGURLProtocol class]];
 [NSURLProtocol registerClass:[ZProtocol class]];

那麼最先執行的是ZProtocol,如果參initWithRequest:返回的為YES,則請求由ZProtocol進行處理,且不會再走ZGURLProtocol。如果ZProtocol的initWithRequest:返回的為NO,則請求繼續向下傳遞由其他的NSURLProtocol子類處理。
一旦返回YES,那麼請求將會由自己寫的子類處理,首先會調用:

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request

這個是一個抽象的方法,子類必須對其實現。通常情況下,我們一般都是直接返回request,但是這裡你也可以直接修改此request,包括header,hosts等。可以對指定request進行重定向操作。
在這裡,我們只是將現有的request進行返回即可。
緊接著,便會開始請求:

- (void)startLoading;

該方法的作用就是開始請求protocol指定的請求。該方法也是protocol子類必須實現的方法。在這裡所做的操作就是:
先判斷是否有緩存數據,如果有,則自己創建NSURLResponse,然後將緩存數據放入,併進行client的一些操作,然後返回;如果沒有緩存數據,則新建一個NSURLConnection,然後發送請求。
先說一下有緩存的情況下:

if (model.data && model.MIMEType) {
        NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:model.MIMEType expectedContentLength:model.data.length textEncodingName:nil];
        [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
        [self.client URLProtocol:self didLoadData:model.data];
        [self.client URLProtocolDidFinishLoading:self];
        return;
    }

(model是緩存數據)有緩存的情況下,直接使用緩存的數據和MIME類型,然後構建NSURLResponse,然後通過協議client調用代理方法。這裡的client是一個protocol,如下:

@protocol NSURLProtocolClient <NSObject>

- (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;

- (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse;

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;

- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;

- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;

- (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

- (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

@end

該協議提供了NSURLProtocol子類與URL Loading System進行溝通的介面。一個APP一定不要去實現這個協議。有緩存的情況下調用回調方法,然後進行處理。
在沒有緩存的情況下:
實例化一個connection,然後發起請求。在我們收到response的時候:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    self.responseData = [[NSMutableData alloc] init];
    self.responseMIMEType = response.MIMEType;
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}

緊接著就是接收數據:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.responseData appendData:data];
    [self.client URLProtocol:self didLoadData:data];
}

接收完數據之後便調用了:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    ZGCacheModel *model = [ZGCacheModel new];
    model.data = self.responseData;
    model.MIMEType = self.responseMIMEType;
    [self setMiType:model.MIMEType withKey:[self.request.URL path]];//userdefault存儲MIMEtype
    
    
    [[ZGUIWebViewCache sharedWebViewCache] setCacheWithKey:self.request.URL.absoluteString value:model];
  
    [self.client URLProtocolDidFinishLoading:self];
}

這個方法是結束家在之後的調用,我們需要在這裡將請求過來的數據進行緩存。這樣我們本地就有了指定URL的返回數據。
這裡還有一個重要的東西沒有介紹,那就是

[NSURLProtocol propertyForKey:ZGURLProtocolKey inRequest:request]
[NSURLProtocol setProperty:@YES forKey:ZGURLProtocolKey inRequest:mutableRequest];

這裡的

+ (void)setProperty:(id)value forKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;

作用是在指定的請求中設置與特定的鍵值相關聯。防止多次調用一個request。
這樣,我們就完成了UIWebView的離線緩存。在這裡我封裝了一個ZGUIWebViewCache。感興趣的可以看一下。

WKWebView的離線緩存處理

WKWebView離線緩存和UIWebView緩存類似,只不過使用WKWebView除了一開始調用一下NSURLProtocol的canInitWithRequest:方法之後,之後的請求似乎就和NSURLProtocol完全無關了,網上都說WKWebView的請求是在獨立的進程里,所以不走NSURLProtocol。這裡是通過NSURLProtocol+WKWebView類進行處理的,詳情可參見:ZGWKWebViewCache
剩下的處理過程就和UIWebView緩存處理類似了。
以上便是對網頁離線緩存的實現。
如有問題,歡迎留言溝通!

參考

1.讓 WKWebView 支持 NSURLProtocol


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有時候我們需要頁面滾動條滾動到某一固定的位置,一般使用Window scrollTo() 方法。 語法就是:scrollTo(xpos,ypos) xpos:必需。要在視窗文檔顯示區左上角顯示的文檔的 x 坐標。 ypos:必需。要在視窗文檔顯示區左上角顯示的文檔的 y 坐標。 例如滾動內容的坐標位 ...
  • DOM.map(callback(index,domElement)); 對匹配元素執行函數對象。 返回值是 jQuery 封裝的數組,使用 get() 來處理返回的對象以得到基礎的數組。 返回數據類型是object。 ...
  • 1. 下載Charles Proxy v4.2.1 版本,百度雲盤下載或去官網下載 2. 安裝後先打開Charles一次(Windows版可以忽略此步驟) 3. 在這個網站(http://charles.iiilab.com/)下載破解文件 charles.jar 4. 替換掉原文件夾里的charl ...
  • 本章目標 一、瞭解Android 二、熟悉Android開發環境 三、可以搭建運行簡單Android項目 四、掌握Android應用開發流程 一、什麼是Android? Android是一個基於Linux的自由及開放源代碼的操作系統 1、利用Linux作為操作系統的內核 2、存儲管理、設備管理、文件 ...
  • 問題 ios真機中Text組件出現多餘邊框(模擬器不會出現,真機會出現該問題)。 原因 在ios啟動頁設置中,預設的尺寸要求與設置中圖片尺寸不符合導致屏幕精度計算出現問題( 啟動屏解析度錯誤設置會導致手機解析度錯誤解析 )。 解決 按照預設尺寸要求設置對應尺寸的啟動頁圖片,確保一一對應。 ...
  • 平時都是用AS敲命令獲取簽名信息。。。還沒有在代碼中獲取過簽名~ 也算是老編程了,沒做過這個稍微有點尷尬。。。本著有好輪子就用的原則,網上找了幾篇博客,這塊內容已經很完善了,我也沒什麼可以優化的。。。 主要參(zhao)考(chao)了http://blog.csdn.net/hcwfc/artic ...
  • 開發中顏色的使用也是非常頻繁的,這裡推薦一個dsxNiubility大牛寫的顏色庫:Wonderful;它的好用就是很清楚的把每個常用的顏色進行了由淺到深的分層,讓我們使用時可以根據自己對顏色的深淺直接取顏色值;其中的功能我比較常用的就是顏色、跑馬燈和顏色漸變,裡面還有其他的功能,感興趣的可以去看下 ...
  • UIButton的預設佈局是:title在右,image在左; 很多時候我們需要的是title在左邊,或者title在下麵,這時就需要調整UIButton的TitleLabel和ImageView的位置了,查了很多資料,要麼零零散散的介紹,要麼就是特別複雜的實現;經過一段時間的學習,在這裡總結一下實 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...