iOS进阶1--UIWebView缓存

来源:互联网 发布:桂林绿源网络 编辑:程序博客网 时间:2024/05/29 07:56

大家有没有遇到过项目有要求对UIWebView的请求数据进行缓存,在有缓存的情况下,从本地加载数据,如果没有缓存,或者缓存已经过期的情况下,则从服务端进行加载数据,并对其返还结果进行缓存操作,如果遇到这种需求,大家会有怎样的解决方案呢?以下是参考http://www.jianshu.com/p/7f3be7c30c77的解决方案。

第一种方案:
原理就是大多数的网络请求都会先调用这个类中的- (NSCachedURLResponse )cachedResponseForRequest:(NSURLRequest )request 这个方法,那我们只要重写这个类,就能达到本地缓存的目的了。
下面是大致的逻辑
1.判断请求中的request 是不是使用get方法,据资料显示一些本地请求的协议也会进到这个方法里面来,所以在第一部,要把不相关的请求排除掉。
2.判断缓存文件夹里面是否存在该文件,如果存在,继续判断文件是否过期,如果过期,则删除。如果文件没有过期,则提取文件,然后组成NSCacheURLResponse返回到方法当中。
3.在有网络的情况下,如果文件夹中不存在该文件,则利用NSConnection这个类发网络请求,再把返回的data和response 数据本地化存储起来,然后组成NSCacheURLResponse返回到方法当中。
具体代码如下:
1.定义一个CustomURLCache 继承于NSURLCache

@interface CustomURLCache : NSURLCache@property(nonatomic, assign) NSInteger cacheTime;@property(nonatomic, retain) NSString *diskPath;@property(nonatomic, retain) NSMutableDictionary *responseDictionary;- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path cacheTime:(NSInteger)cacheTime;@end

.m文件如下:

#import "CustomURLCache.h"#import "Reachability.h"@interface CustomURLCache(private)- (NSString *)cacheFolder;- (NSString *)cacheFilePath:(NSString *)file;- (NSString *)cacheRequestFileName:(NSString *)requestUrl;- (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl;- (NSCachedURLResponse *)dataFromRequest:(NSURLRequest *)request;- (void)deleteCacheFolder;@end@implementation CustomURLCache@synthesize cacheTime = _cacheTime;@synthesize diskPath = _diskPath;@synthesize responseDictionary = _responseDictionary;/**初始化方法 @param memoryCapacity 缓存内存大小,默认4M,4* 1024 * 1024 @param diskCapacity 硬盘大小,默认20M,20 * 1024 * 1024 @param path 缓存路径,默认(NSHomeDirectory)/Library/Caches/(current application name, [[NSProcessInfo processInfo] processName]) @param cacheTime 缓存有效时间 @return 自定义NSURLCache对象 */- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path cacheTime:(NSInteger)cacheTime {    if (self = [self initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path]) {        self.cacheTime = cacheTime;        if (path)            self.diskPath = path;        else            self.diskPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];        self.responseDictionary = [NSMutableDictionary dictionaryWithCapacity:0];    }    return self;}- (void)dealloc {    [_diskPath release];    [_responseDictionary release];    [super dealloc];}//所有网络请求都会优先调用该方法,所有我们只要在这里重载这个方法,对请求进行处理即可- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {    if ([request.HTTPMethod compare:@"GET"] != NSOrderedSame) {        return [super cachedResponseForRequest:request];    }    //关键方法    return [self dataFromRequest:request];}- (void)removeAllCachedResponses {    [super removeAllCachedResponses];    [self deleteCacheFolder];}- (void)removeCachedResponseForRequest:(NSURLRequest *)request {    [super removeCachedResponseForRequest:request];    NSString *url = request.URL.absoluteString;    NSString *fileName = [self cacheRequestFileName:url];    NSString *otherInfoFileName = [self cacheRequestOtherInfoFileName:url];    NSString *filePath = [self cacheFilePath:fileName];    NSString *otherInfoPath = [self cacheFilePath:otherInfoFileName];    NSFileManager *fileManager = [NSFileManager defaultManager];    [fileManager removeItemAtPath:filePath error:nil];    [fileManager removeItemAtPath:otherInfoPath error:nil];}#pragma mark - custom url cache- (NSString *)cacheFolder {    return @"URLCACHE";}- (void)deleteCacheFolder {    NSString *path = [NSString stringWithFormat:@"%@/%@", self.diskPath, [self cacheFolder]];    NSFileManager *fileManager = [NSFileManager defaultManager];    [fileManager removeItemAtPath:path error:nil];}- (NSString *)cacheFilePath:(NSString *)file {    NSString *path = [NSString stringWithFormat:@"%@/%@", self.diskPath, [self cacheFolder]];    NSFileManager *fileManager = [NSFileManager defaultManager];    BOOL isDir;    if ([fileManager fileExistsAtPath:path isDirectory:&isDir] && isDir) {    } else {        [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];    }    return [NSString stringWithFormat:@"%@/%@", path, file];}- (NSString *)cacheRequestFileName:(NSString *)requestUrl {    return [Util md5Hash:requestUrl];}- (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl {    return [Util md5Hash:[NSString stringWithFormat:@"%@-otherInfo", requestUrl]];}/*关键方法,如果没有缓存,则返回nil,有则返回一个NSCachedURLResponse对象*/- (NSCachedURLResponse *)dataFromRequest:(NSURLRequest *)request {    NSString *url = request.URL.absoluteString;    NSString *fileName = [self cacheRequestFileName:url];//缓存文件夹名称    NSString *otherInfoFileName = [self cacheRequestOtherInfoFileName:url];//请它文件名称    NSString *filePath = [self cacheFilePath:fileName];//缓存文件路径    NSString *otherInfoPath = [self cacheFilePath:otherInfoFileName];//其它文件路径    NSDate *date = [NSDate date];//缓存操作时间    NSFileManager *fileManager = [NSFileManager defaultManager];    if ([fileManager fileExistsAtPath:filePath]) {        //缓存文件夹已经存在        BOOL expire = false;        NSDictionary *otherInfo = [NSDictionary dictionaryWithContentsOfFile:otherInfoPath];        if (self.cacheTime > 0) {        //cacheTime == 0,则表示永久有效            NSInteger createTime = [[otherInfo objectForKey:@"time"] intValue];            if (createTime + self.cacheTime < [date timeIntervalSince1970]) {                expire = true;            }        }        if (expire == false) {            NSLog(@"data from cache ...");            //从缓存读取数据,初始化一个NSCachedURLResponse对象            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] autorelease];            [response release];            return cachedResponse;        } else {            NSLog(@"cache expire ... ");            //缓存过期,移除已经缓存的数据            [fileManager removeItemAtPath:filePath error:nil];            [fileManager removeItemAtPath:otherInfoPath error:nil];        }    }    if (![Reachability networkAvailable]) {        return nil;    }    //sendSynchronousRequest请求也要经过NSURLCache,从新进行数据缓存    __block NSCachedURLResponse * cachedResponse = nil;    id boolExsite = [self.responseDictionary objectForKey:url];    if (boolExsite == nil) {        [self.responseDictionary setValue:[NSNumber numberWithBool:TRUE] forKey:url];       // NSURLResponse *response = nil;//        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];        [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {            if (response && data) {                [self.responseDictionary removeObjectForKey:url];            }            if (connectionError) {                NSLog(@"error : %@", connectionError);                NSLog(@"not cached: %@", request.URL.absoluteString);                cachedResponse = nil;            }            NSLog(@"get request ... ");            //save to cache            NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%f", [date timeIntervalSince1970]], @"time",                                  response.MIMEType, @"MIMEType",                                  response.textEncodingName, @"textEncodingName", nil];            [dict writeToFile:otherInfoPath atomically:YES];            [data writeToFile:filePath atomically:YES];          cachedResponse = [[[NSCachedURLResponse alloc] initWithResponse:response data:data] autorelease];            }];                return cachedResponse;    }    return nil;}@end

具体使用如下:

//关键:类初始化时进行,改变默认的NSURLCache- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {        CustomURLCache *urlCache = [[CustomURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024                                                                     diskCapacity:200 * 1024 * 1024                                                                         diskPath:nil                                                                        cacheTime:0];         //setSharedURLCache该方法可以使缓存类变为我们自定义的缓存方案                                                      [CustomURLCache setSharedURLCache:urlCache];        [urlCache release];    }    return self;}- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.frame];    webView.delegate = self;    self.webView = webView;    [webView release];    [self.view addSubview:_webView];    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/"]]];}- (void)didReceiveMemoryWarning{    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.    CustomURLCache *urlCache = (CustomURLCache *)[NSURLCache sharedURLCache];    [urlCache removeAllCachedResponses];}- (void)dealloc {    [_webView release];    [super dealloc];}#pragma mark - webview- (void)webViewDidFinishLoad:(UIWebView *)webView {    [MBProgressHUD hideHUDForView:self.view animated:YES];}- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {    [MBProgressHUD hideHUDForView:self.view animated:YES];}- (void)webViewDidStartLoad:(UIWebView *)webView {    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];    hud.mode = MBProgressHUDModeIndeterminate;    hud.labelText = @"Loading...";}

注意: 这方案会返回多次 每一次链接相同的url(有网络的情况下,部分网页如:(百度),它这个url里面可能内嵌了很多其他的url,那其他的url可能每次都不一样,所以返回的request.url.absluteString 都不一样,这样导致每次系统会根据absoluteStr 来创建文件,则会越来越多;这种情况下是不适用的。

源码地址:https://github.com/lzhlewis2015/UIWebViewLocalCache

第二种方案:

+ (instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;//通过上面方法进行设置NSURLRequestCachePolicy- (void)loadWebView{   //可以每次请求的时候先去缓存里面读取,如果缓存里面没有的话再去网上加载    request = [NSURLRequest requestWithURL:urlcachePolicy:NSURLRequestReturnCacheDataElseLad timeoutInterval:10.];    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];    if (cachedResponse) {     //cache已经存在 直接用web加载本地的数据       NSLog(@"cache存在 直接加载本地的数据");      [_WKWebView loadHTMLString:[[NSString alloc]initWithData:cachedResponse.data encoding:NSUTF8StringEncoding] baseURL:_request.URL];}}else {    //cache还不存在    NSLog(@"cache不存在 需要从网络加载");    NSURLSessionDownloadTask *task = [[NSURLSession sharedSession]downloadTaskWithRequest:_request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {    NSData *data = [NSData dataWithContentsOfURL:location];    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:_request];    if ((!cachedResponse) && response && data) {     NSCachedURLResponse *cachedURLResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];    [[NSURLCache sharedURLCache]storeCachedResponse:cachedURLResponse forRequest:_request];} }];   [task resume];   [_WKWebView loadRequest:request];}}//这个清除缓存也很简单 [[NSURLCache sharedURLCache] removeCachedResponseForRequest:request];
0 0
原创粉丝点击