可以先下載Demo看看效果,Github地址:< GitHub – ming1016/STMURLCache: iOS預載入Web頁面方案 >
可以預載入多個網址,然後在離線狀態去顯示那幾個網址,看看是不是都完全快取下來了。
使用方法
在需要開啟預載入的地方建立
1 2 3 |
self.sCache = [STMURLCache create:^(STMURLCacheMk *mk) { mk.whiteListsHost(whiteLists).whiteUserAgent(@"starming"); }]; |
這裡是所有可設定專案,預設設定可以檢視 model 的 get 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (STMURLCacheMk *(^)(NSUInteger)) memoryCapacity; //記憶體容量 - (STMURLCacheMk *(^)(NSUInteger)) diskCapacity; //本地儲存容量 - (STMURLCacheMk *(^)(NSUInteger)) cacheTime; //快取時間 - (STMURLCacheMk *(^)(NSString *)) subDirectory; //子目錄 - (STMURLCacheMk *(^)(BOOL)) isDownloadMode; //是否啟動下載模式 - (STMURLCacheMk *(^)(NSArray *)) whiteListsHost; //域名白名單 - (STMURLCacheMk *(^)(NSString *)) whiteUserAgent; //WebView的user-agent白名單 - (STMURLCacheMk *(^)(NSString *)) addHostWhiteList; //新增一個域名白名單 - (STMURLCacheMk *(^)(NSString *)) addRequestUrlWhiteList; //新增請求白名單 //NSURLProtocol相關設定 - (STMURLCacheMk *(^)(BOOL)) isUsingURLProtocol; //是否使用NSURLProtocol,預設使用NSURLCache |
也可以隨時更新這些設定項
1 2 3 |
[self.sCache update:^(STMURLCacheMk *mk) { mk.isDownloadMode(YES); }]; |
預載入名單可以按照整個 web 頁面請求進行預載入
1 |
[self.sCache preLoadByWebViewWithUrls:@[@"http://www.v2ex.com",@"http://www.github.com"]; |
如果需要按照單個資源列表進行預載入可以使用 preLoadByRequestWithUrls 這個方法。
白名單設定
對於只希望快取特定域名或者地址的可以通過白名單進行設定,可以在建立時進行設定或者更新時設定。
1 2 3 4 5 |
NSString *whiteListStr = @"www.starming.com|www.github.com|www.v2ex.com|www.baidu.com"; NSMutableArray *whiteLists = [NSMutableArray arrayWithArray:[whiteListStr componentsSeparatedByString:@"|"]]; self.sCache = [STMURLCache create:^(STMURLCacheMk *mk) { mk.whiteListsHost(whiteLists).whiteUserAgent(@"starming"); }]; |
這裡的 whiteUserAgent 的設定會設定 webview 的 UserAgent,這樣能夠讓webview以外的網路請求被過濾掉。
基本載入快取實現原理
建立 STMURLCache 後設定 NSURLCache 的 URLCache ,在 cachedResponseForRequest 方法中獲取 NSURLRequest 判斷白名單,檢驗是否有與之對應的 Cache ,有就使用本地資料返回 NSCachedURLResponse ,沒有就通過網路獲取資料資料快取。 STMURLCache 物件釋放時將 NSURLCache 設定為不快取,表示這次預載入完成不需要再快取。當快取空間超出設定大小會將其清空。
使用 NSURLProtocol 這種原理基本類似。
白名單實現原理
建立域名列表設定項 whiteListsHost 和 userAgent 設定項,在建立和更新時對其進行設定。在網路請求開始通過設定項進行過濾。具體實現如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//對於域名白名單的過濾 if (self.mk.cModel.whiteListsHost.count > 0) { id isExist = [self.mk.cModel.whiteListsHost objectForKey:[self hostFromRequest:request]]; if (!isExist) { return nil; } } //User-Agent來過濾 if (self.mk.cModel.whiteUserAgent.length > 0) { NSString *uAgent = [request.allHTTPHeaderFields objectForKey:@"User-Agent"]; if (uAgent) { if (![uAgent hasSuffix:self.mk.cModel.whiteUserAgent]) { return nil; } } } |
具體快取實現
快取的實現有兩種,一種是 NSURLCache 另一種是 NSURLProtocol , STMURLCache 同時支援了這兩種,通過 STMURLCacheModel 裡的 isUsingURLProtocol 設定項來選擇使用哪個。
NSURLCache的實現
沒有快取的 request 會對其進行請求將獲取資料按照hash地址存兩份於本地,一份是資料,一份記錄時間和型別,時間記錄可以用於判斷失效時間。對於判斷是否有快取可以根據請求地址對應的檔案進行判斷。具體實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (NSCachedURLResponse *)localCacheResponeWithRequest:(NSURLRequest *)request { __block NSCachedURLResponse *cachedResponse = nil; NSString *filePath = [self filePathFromRequest:request isInfo:NO]; NSString *otherInfoPath = [self filePathFromRequest:request isInfo:YES]; NSDate *date = [NSDate date]; NSFileManager *fm = [NSFileManager defaultManager]; if ([fm fileExistsAtPath:filePath]) { //有快取檔案的情況 BOOL expire = false; NSDictionary *otherInfo = [NSDictionary dictionaryWithContentsOfFile:otherInfoPath]; if (self.cacheTime > 0) { NSInteger createTime = [[otherInfo objectForKey:@"time"] integerValue]; if (createTime + self.cacheTime |
NSURLProtocol的實現
在設定配置項和更新配置項時需要建立一個 STMURLCacheModel 的單例來進行設定和更新配置項給 NSURLProtocol 的實現來使用。通過 isUsingURLProtocol 設定項區分, NSURLProtocol 是通過registerClass方式將protocol實現的進行註冊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
- (STMURLCache *)configWithMk { self.mk.cModel.isSavedOnDisk = YES; if (self.mk.cModel.isUsingURLProtocol) { STMURLCacheModel *sModel = [STMURLCacheModel shareInstance]; sModel.cacheTime = self.mk.cModel.cacheTime; sModel.diskCapacity = self.mk.cModel.diskCapacity; sModel.diskPath = self.mk.cModel.diskPath; sModel.cacheFolder = self.mk.cModel.cacheFolder; sModel.subDirectory = self.mk.cModel.subDirectory; sModel.whiteUserAgent = self.mk.cModel.whiteUserAgent; sModel.whiteListsHost = self.mk.cModel.whiteListsHost; [NSURLProtocol registerClass:[STMURLProtocol class]]; } else { [NSURLCache setSharedURLCache:self]; } return self; } |
關閉時兩者也是不同的,通過設定項進行區分
1 2 3 4 5 6 7 8 9 |
- (void)stop { if (self.mk.cModel.isUsingURLProtocol) { [NSURLProtocol unregisterClass:[STMURLProtocol class]]; } else { NSURLCache *c = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:c]; } [self.mk.cModel checkCapacity]; } |
白名單處理還有讀取快取和前者都類似,但是在快取Data時 NSURLCached 的方案裡是通過發起一次新的請求來獲取資料,而 NSURLProtocol 在 NSURLConnection 的 Delegate 裡可以獲取到,少了一次網路的請求,這裡需要注意的是在 – (void) connection:(NSURLConnection )connection didReceiveData:(NSData )data 每次從這個回撥裡獲取的資料不是完整的,要在 – (void) connectionDidFinishLoading:(NSURLConnection *)connection 這個會調裡將分段資料拼接成完整的資料儲存下來。具體完整的程式碼實現可以看 STMURLProtocol 裡的程式碼實現。
後記
通過 map 網路請求可以快取請求,也可以 mock 介面請求進行測試。