Giter VIP home page Giter VIP logo

stmurlcache's Introduction

iOS预加载Web页面方案

可以先下载Demo看看效果,Github地址:< GitHub - ming1016/STMURLCache: iOS预加载Web页面方案 > 可以预加载多个网址,然后在离线状态去显示那几个网址,看看是不是都完全缓存下来了。

使用方法

在需要开启预加载的地方创建

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:@[@"http://www.v2ex.com",@"http://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 另一种是 NSURLProtocolSTMURLCache 同时支持了这两种,通过 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 接口请求进行测试。

完整代码:< GitHub - ming1016/STMURLCache: iOS预加载Web页面方案 >

stmurlcache's People

Contributors

ming1016 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stmurlcache's Issues

webview预加载

我把 [self.sCache preLoadByWebViewWithUrls:@[adURL]];
方法放到- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions这里
有的时候程序会报错

戴老师,请教下个问题

发现有些坑.使用wkwebView,发现post请求参数body丢失问题,查看yeatse/NSURLProtocol-WebKitSupport#7, 说可以跟服务端定个协议,WebP 图片改成类似 webp://xxx.com/xxx 这样的形式,然后注册名为 webp 的 scheme 来拦截,但是这个需求服务端那边可能不太愿意改因为太多了。想问下使用WKwebView有没有更好的方案可以解决预加载出现的问题,更快速可以加载网页显示速度.

戴老师,有几个地方可能有坑

  1. 使用 webview 加载页面没禁用js,可能某些js执行会带来问题,比如弹窗之类。
  2. 中缓存后的 response 使用 NSURLResponse。webview中如果有ajax请求,会导致拿不到正确的header,比如statuscode之类的关键信息。通常ajax异步获取资源都有header的判断,这种情况使用 NSHTTPURLResponse 可能更好。

NSURLProtocol方式

NSURLProtocol 方式的话 会把平时的http的get请求缓存吧 是不是不安全呢

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.