iOS預加載Web頁面方案

使用方法

码报开奖结果本期 www.iwqgw.icu 在需要開啟預加載的地方創建

self.sCache = [STMURLCache create:^(STMURLCacheMk *mk) {
    mk.whiteListsHost(whiteLists).whiteUserAgent(@"starming");}];

這里是所有可設置項目,默認設置可以查看 model 的 get 方法

- (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

也可以隨時更新這些設置項

[self.sCache update:^(STMURLCacheMk *mk) {
    mk.isDownloadMode(YES);}];

預加載名單可以按照整個 web 頁面請求進行預加載

[self.sCache preLoadByWebViewWithUrls:@[@"//www.v2ex.com",@"//www.github.com"];

如果需要按照單個資源列表進行預加載可以使用 preLoadByRequestWithUrls 這個方法。

白名單設置

對于只希望緩存特定域名或者地址的可以通過白名單進行設置,可以在創建時進行設置或者更新時設置。

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 后設置 NSURLCacheURLCache ,在 cachedResponseForRequest 方法中獲取 NSURLRequest 判斷白名單,檢驗是否有與之對應的 Cache ,有就使用本地數據返回 NSCachedURLResponse ,沒有就通過網絡獲取數據數據緩存。 STMURLCache 對象釋放時將 NSURLCache 設置為不緩存,表示這次預加載完成不需要再緩存。當緩存空間超出設置大小會將其清空。

使用 NSURLProtocol 這種原理基本類似。

白名單實現原理

創建域名列表設置項 whiteListsHostuserAgent 設置項,在創建和更新時對其進行設置。在網絡請求開始通過設置項進行過濾。具體實現如下

//對于域名白名單的過濾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地址存兩份于本地,一份是數據,一份記錄時間和類型,時間記錄可以用于判斷失效時間。對于判斷是否有緩存可以根據請求地址對應的文件進行判斷。具體實現如下:

- (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 < [date timeIntervalSince1970]) {
                expire = true;
            }
        }        if (expire == false) {            //從緩存里讀取數據
            NSData *data = [NSData dataWithContentsOfFile:filePath];            NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[otherInfo objectForKey:@"MIMEType"] expectedContentLength:data.length textEncodingName:[otherInfo objectForKey:@"textEncodingName"]];            NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];            return cachedResponse;
        } else {            //cache失效了
            [fm removeItemAtPath:filePath error:nil];      //清除緩存data
            [fm removeItemAtPath:otherInfoPath error:nil]; //清除緩存其它信息
            return nil;
        }
    } else {        //從網絡讀取
        self.isSavedOnDisk = NO;        id isExist = [self.responseDic objectForKey:request.URL.absoluteString];        if (isExist == nil) {
            [self.responseDic setValue:[NSNumber numberWithBool:TRUE] forKey:request.URL.absoluteString];            NSURLSession *session = [NSURLSession sharedSession];            NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {                if (error) {
                    cachedResponse = nil;
                } else {                    NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%f",[date timeIntervalSince1970]],@"time",response.MIMEType,@"MIMEType",response.textEncodingName,@"textEncodingName", nil];                    BOOL resultO = [dic writeToFile:otherInfoPath atomically:YES];                    BOOL result = [data writeToFile:filePath atomically:YES];                    if (resultO == NO || result == NO) {
                    } else {
                    }
                    cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
                }
            }];
            [task resume];            return cachedResponse;
        }        return nil;
    }
}

NSURLProtocol的實現

在設置配置項和更新配置項時需要創建一個 STMURLCacheModel 的單例來進行設置和更新配置項給 NSURLProtocol 的實現來使用。通過 isUsingURLProtocol 設置項區分, NSURLProtocol 是通過registerClass方式將protocol實現的進行注冊。

- (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;
}

關閉時兩者也是不同的,通過設置項進行區分

- (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 的方案里是通過發起一次新的請求來獲取數據,而 NSURLProtocolNSURLConnection 的 Delegate 里可以獲取到,少了一次網絡的請求,這里需要注意的是在 - (void) connection:(NSURLConnection )connection didReceiveData:(NSData )data 每次從這個回調里獲取的數據不是完整的,要在 - (void) connectionDidFinishLoading:(NSURLConnection *)connection 這個會調里將分段數據拼接成完整的數據保存下來。具體完整的代碼實現可以看 STMURLProtocol 里的代碼實現。

后記

通過 map 網絡請求可以緩存請求,也可以 mock 接口請求進行測試。



文/星光社的戴銘(簡書作者)


上一篇: 蘋果發布iOS 10.2.1 beta2!更新容量大幅縮水!

下一篇: iOS中的各種鎖

分享到: 更多
玩极速赛车的方法 炸金花心理技巧 重庆时时彩怎么作弊的 重庆时时官网下载 云南时时规则 幸运28有什么稳赚法 二人麻将下载不联网下载 三公网站 麻将下载 快乐时时计划软件手机版下载 怎么推广博彩 北京pk10走势图怎么分析 重庆龙虎计划软件 欢乐四川麻将 pk10如何将100玩到一万 百盈彩票是黑平台吗