iOS webView緩存,保證加載最新html

loading.png

iOS webView緩存,保證加載最新html

[TOC]

前言

最近有個需求,修改webview(WKWebview)加載的緩存機制。因現(xiàn)在使用的緩存機制是NSURLRequestReturnCacheDataElseLoad(NSURLRequest的緩存機制下面會說到)。這個緩存機制就是只有當(dāng)本地緩存不存在的時候才會請求,否則加載本地緩存,這樣就導(dǎo)致當(dāng)html有所修改的話,下次進入不能主動刷新網(wǎng)頁,還是加載的緩存,需要手動刷新才能看到最新內(nèi)容。現(xiàn)在的要求就是當(dāng):當(dāng)html過期后(html有修改),在下次主動加載html的時候自動加載最新內(nèi)容。

尋找解決方案

1. 查看NSURLRequestAPI

既然之前使用了NSURLRequest的緩存機制,那么首先想到的就是看看有沒有對應(yīng)的緩存機制。

typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    NSURLRequestUseProtocolCachePolicy = 0,//默認遵守http緩存策略

    NSURLRequestReloadIgnoringLocalCacheData = 1, //忽略本地緩存
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented //忽略本地和遠程緩存
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

    NSURLRequestReturnCacheDataElseLoad = 2,//只有當(dāng)本地緩存不存在的時候才會請求,否則加載本地緩存
    NSURLRequestReturnCacheDataDontLoad = 3,//只加載本地緩存,沒有緩存也不會請求

    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented //判斷緩存是否過期
};

忽略Unimplemented,可以看到NSURLRequestReloadRevalidatingCacheData不正是我們需要的緩存策略嗎?當(dāng)你高高興興的將緩存策略設(shè)置為NSURLRequestReloadRevalidatingCacheData后,然后加載html,然后修改html內(nèi)容,發(fā)現(xiàn)確實會加載最新的。這個時候你一定會很高心,然而當(dāng)你打印html加載時間的時候,你會發(fā)現(xiàn)html未修改的情況下和不加載緩存所用的時間都是一樣的,其結(jié)論就是并沒有加載緩存。這個時候你再看Unimplemented就會煥然大悟了。

所以通過修改NSURLRequest的緩存策略是無法實現(xiàn)該功能的,pass

2. 網(wǎng)上搜索webView的緩存加載策略

通過設(shè)置NSURLRequest的緩存機制無法達到我們的目的。沒辦法,只有找其他的方法了。
在查看了很多篇相關(guān)的技術(shù)博客后,終于找到了一個方法,就是設(shè)置ETag/If-None-MatchLast-Modified/If-Modified-Since來判斷html內(nèi)容是否有更新。其中If-None-MatchIf-Modified-Since是設(shè)置在request headers請求頭中,ETagLast-Modifiedresponse headers響應(yīng)頭中,由服務(wù)器返回的。

參數(shù)介紹

  • ETag:服務(wù)器驗證令牌,文件內(nèi)容hash。
  • Last-Modified:響應(yīng)頭標(biāo)識了資源的最后修改時間。
  • If-None-Match:比較ETag是否一致。
  • If-Modified-Since:比較資源最后修改的時間是否一致。

關(guān)于html的緩存策略可以看看這篇博客,講的很詳細

基本實現(xiàn)原理

  1. 第一次請求某個html的時候,響應(yīng)頭response headers中會返回ETagLast-Modified(需要html做設(shè)置),將其記錄下來。
  2. 后面每次請求時,在request headers請求頭中設(shè)置If-None-MatchIf-Modified-Since,其中If-None-Match就是記錄的ETag值,If-Modified-Since就是記錄的Last-Modified值。該值會和服務(wù)端的ETagLast-Modified比較。如果相同則返回狀態(tài)碼304,說明沒有更新,否則返回200,說明需要重新請求。

iOS實現(xiàn)方式

NSURL *url = [NSURL URLWithString:@"http://172.17.124.102:8888/webViewTest.html"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
    request.HTTPMethod = @"HEAD";
    //獲取記錄的response headers
    NSDictionary *cachedHeaders = [[NSUserDefaults standardUserDefaults] objectForKey:url.absoluteString];
    //設(shè)置request headers
    if (cachedHeaders) {
        NSString *etag = [cachedHeaders objectForKey:@"Etag"];
        if (etag) {
            [request setValue:etag forHTTPHeaderField:@"If-None-Match"];
        }
        NSString *lastModified = [cachedHeaders objectForKey:@"Last-Modified"];
        if (lastModified) {
            [request setValue:lastModified forHTTPHeaderField:@"If-Modified-Since"];
        }
    }
    
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"======= %f",[[NSDate date] timeIntervalSince1970] * 1000);
        // 類型轉(zhuǎn)換
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"statusCode == %@", @(httpResponse.statusCode));
        // 判斷響應(yīng)的狀態(tài)碼
        if (httpResponse.statusCode == 304 || httpResponse.statusCode == 0) {
            //如果狀態(tài)碼為304或者0(網(wǎng)絡(luò)不通?),則設(shè)置request的緩存策略為讀取本地緩存
            [request setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
        }else {
            //如果狀態(tài)碼為200,則保存本次的response headers,并設(shè)置request的緩存策略為忽略本地緩存,重新請求數(shù)據(jù)
            [[NSUserDefaults standardUserDefaults] setObject:httpResponse.allHeaderFields forKey:request.URL.absoluteString];
            //如果狀態(tài)碼為200,則設(shè)置request的緩存策略為忽略本地緩存
            [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
        }
        
        //未更新的情況下讀取緩存
        dispatch_async(dispatch_get_main_queue(), ^{
            //判斷結(jié)束之后,修改請求方式,加載網(wǎng)頁
            request.HTTPMethod = @"GET";
            [self.webView loadRequest:request];
        });
    }] resume];

在這里,我的實現(xiàn)方式是在每次請求加載之前,先獲取html的response headers響應(yīng)頭(使用'HEAD'請求方式,只獲取'response headers',不獲取頁面),通過返回的狀態(tài)碼最終確定其緩存策略是讀取本地緩存還是重新加載。最終達到了預(yù)期的效果。

最后

雖然通過這個方式實現(xiàn)了該功能,但是在實現(xiàn)過程中還是有一些東西沒有弄懂。
比如:

  1. webView通過loadRequest加載html的時候,設(shè)置了request headers,然后在WKWebView的代理方法webView:didFinishNavigation:方法中獲取的狀態(tài)碼永遠是200,response headers響應(yīng)頭在修改了html內(nèi)容后都沒有變化,這里獲取到的數(shù)據(jù)和通過NSURLSession獲取到的有什么不同。
    2. 還有就是這種方式獲取狀態(tài)碼response headers響應(yīng)頭其實相當(dāng)于在加載之前重新請求了一下。 使用HEAD請求可以避免網(wǎng)頁的二次下載,只請求響應(yīng)頭數(shù)據(jù),謝謝王洪亮ios的提醒。

不知道有沒有更好的方法來實現(xiàn)該功能,歡迎討論和指正。

參考博客

  1. http://imweb.io/topic/5795dcb6fb312541492eda8c
  2. https://blog.cnbluebox.com/blog/2015/05/07/architecture-ios-1/
  3. http://www.itdecent.cn/p/ebcb0a1823be#comment-23319974
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容