iOS webView緩存,保證加載最新html
[TOC]
前言
最近有個需求谎脯,修改webview(WKWebview)
加載的緩存機制葱跋。因現(xiàn)在使用的緩存機制是NSURLRequestReturnCacheDataElseLoad
(NSURLRequest
的緩存機制下面會說到)。這個緩存機制就是只有當本地緩存不存在的時候才會請求源梭,否則加載本地緩存
娱俺,這樣就導(dǎo)致當html有所修改的話,下次進入不能主動刷新網(wǎng)頁废麻,還是加載的緩存荠卷,需要手動刷新才能看到最新內(nèi)容。現(xiàn)在的要求就是當:當html過期后(html有修改)烛愧,在下次主動加載html的時候自動加載最新內(nèi)容油宜。
尋找解決方案
1. 查看NSURLRequest
的API
既然之前使用了NSURLRequest
的緩存機制,那么首先想到的就是看看有沒有對應(yīng)的緩存機制怜姿。
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
NSURLRequestUseProtocolCachePolicy = 0,//默認遵守http緩存策略
NSURLRequestReloadIgnoringLocalCacheData = 1, //忽略本地緩存
NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented //忽略本地和遠程緩存
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2,//只有當本地緩存不存在的時候才會請求慎冤,否則加載本地緩存
NSURLRequestReturnCacheDataDontLoad = 3,//只加載本地緩存,沒有緩存也不會請求
NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented //判斷緩存是否過期
};
忽略Unimplemented
沧卢,可以看到NSURLRequestReloadRevalidatingCacheData
不正是我們需要的緩存策略嗎蚁堤?當你高高興興的將緩存策略設(shè)置為NSURLRequestReloadRevalidatingCacheData
后,然后加載html但狭,然后修改html內(nèi)容披诗,發(fā)現(xiàn)確實會加載最新的。這個時候你一定會很高心立磁,然而當你打印html加載時間的時候呈队,你會發(fā)現(xiàn)html未修改的情況下和不加載緩存所用的時間都是一樣的,其結(jié)論就是并沒有加載緩存息罗。這個時候你再看Unimplemented
就會煥然大悟了掂咒。
所以通過修改NSURLRequest的緩存策略是無法實現(xiàn)該功能的,pass
2. 網(wǎng)上搜索webView
的緩存加載策略
通過設(shè)置NSURLRequest
的緩存機制無法達到我們的目的迈喉。沒辦法绍刮,只有找其他的方法了。
在查看了很多篇相關(guān)的技術(shù)博客后挨摸,終于找到了一個方法孩革,就是設(shè)置ETag/If-None-Match
和Last-Modified/If-Modified-Since
來判斷html內(nèi)容是否有更新。其中If-None-Match
和If-Modified-Since
是設(shè)置在request headers
請求頭中得运,ETag
和Last-Modified
是response headers
響應(yīng)頭中膝蜈,由服務(wù)器返回的锅移。
參數(shù)介紹
-
ETag
:服務(wù)器驗證令牌,文件內(nèi)容hash
饱搏。 -
Last-Modified
:響應(yīng)頭標識了資源的最后修改時間非剃。 -
If-None-Match
:比較ETag
是否一致备绽。 -
If-Modified-Since
:比較資源最后修改的時間是否一致。
關(guān)于html的緩存策略可以看看這篇博客鬓催,講的很詳細
基本實現(xiàn)原理
- 第一次請求某個html的時候,響應(yīng)頭
response headers
中會返回ETag
和Last-Modified
(需要html做設(shè)置)宇驾,將其記錄下來倍靡。 - 后面每次請求時,在
request headers
請求頭中設(shè)置If-None-Match
和If-Modified-Since
,其中If-None-Match
就是記錄的ETag
值,If-Modified-Since
就是記錄的Last-Modified
值。該值會和服務(wù)端的ETag
和Last-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)過程中還是有一些東西沒有弄懂。
比如:
- 在
webView
通過loadRequest
加載html
的時候跨扮,設(shè)置了request headers
序无,然后在WKWebView
的代理方法webView:didFinishNavigation:
方法中獲取的狀態(tài)碼
永遠是200
,response headers
響應(yīng)頭在修改了html
內(nèi)容后都沒有變化衡创,這里獲取到的數(shù)據(jù)和通過NSURLSession
獲取到的有什么不同帝嗡。
2. 還有就是這種方式獲取使用HEAD請求可以避免網(wǎng)頁的二次下載璃氢,只請求響應(yīng)頭數(shù)據(jù)哟玷,謝謝王洪亮ios的提醒。狀態(tài)碼
及response headers
響應(yīng)頭其實相當于在加載之前重新請求了一下。
不知道有沒有更好的方法來實現(xiàn)該功能一也,歡迎討論和指正裁僧。