UIWebView---UIWebView实现离线浏览

来源:互联网 发布:程序员是吃青春饭吗 编辑:程序博客网 时间:2024/05/16 12:44

参考

http://re-reference.iteye.com/blog/1391408

http://nshipster.cn/nsurlcache/

http://blog.csdn.net/mad2man/article/details/30285001


SDK里绝大部分的网络请求都会访问[NSURLCache sharedURLCache]这个对象,它的cachedResponseForRequest:方法会返回一个NSCachedURLResponse对象。如果这个NSCachedURLResponse对象不为nil,且没有过期,那么就使用这个缓存的响应,否则就发起一个不访问缓存的请求。 

要注意的是NSCachedURLResponse对象不能被提前释放,除非UIWebView去调用NSURLCache的removeCachedResponseForRequest:方法,原因貌似是UIWebView并不retain这个响应。而这个问题又很头疼,因为UIWebView有内存泄露的嫌疑,即使它被释放了,也很可能不去调用上述方法,于是内存就一直占用着了。

顺便说下NSURLRequest对象,它有个cachePolicy属性,只要其值为NSURLRequestReloadIgnoringLocalCacheData的话,就不会访问缓存。可喜的是这种情况貌似只有在缓存里没取到,或是强制刷新时才可能出现。 
实际上NSURLCache本身就有磁盘缓存功能,然而在iOS上,NSCachedURLResponse却被限制为不能缓存到磁盘(NSURLCacheStorageAllowed被视为NSURLCacheStorageAllowedInMemoryOnly)。 
不过既然知道了原理,那么只要自己实现一个NSURLCache的子类,然后改写cachedResponseForRequest:方法,让它从硬盘读取缓存即可。 

第一种重写NSURLCache修改cachedResponseForRequest

代码这边有UIWebView离线缓存
关键代码 重写cachedResponseForRequest
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {    if ([request.HTTPMethod compare:@"GET"] != NSOrderedSame) {        return [super cachedResponseForRequest:request];    }    NSURL *url = request.URL;    if (![supportSchemes containsObject:url.scheme]) {        return [super cachedResponseForRequest:request];    }    return [self dataFromRequest:request];}- (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) {            NSInteger createTime = [[otherInfo objectForKey:@"time"] intValue];            if (createTime + self.cacheTime < [date timeIntervalSince1970]) {                expire = true;            }        }                if (expire == false) {            NSLog(@"data from cache ...");                        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;    }    // 如果缓存已经过期 而且有网络的话    __block NSCachedURLResponse *cachedResponse = nil;    //sendSynchronousRequest请求也要经过NSURLCache    id boolExsite = [self.responseDictionary objectForKey:url];    if (boolExsite == nil) {        [self.responseDictionary setValue:[NSNumber numberWithBool:TRUE] forKey:url];          [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data,NSError *error)        {            if (response && data) {                                [self.responseDictionary removeObjectForKey:url];                                if (error) {                    NSLog(@"error : %@", error);                    NSLog(@"not cached: %@", request.URL.absoluteString);                    cachedResponse = nil;                }                                NSLog(@"cache url --- %@ ",url);                                //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;        //NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];                    }    return nil;}

通过MD5hash生成文件名
MD5hash,哈希值计算器,是一款md5校验工具。每个文件都可以用Hash MD5验证程序算出一个固定的MD5码来。
- (NSString *)cacheRequestFileName:(NSString *)requestUrl {    return [Util md5Hash:requestUrl];}- (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl {    return [Util md5Hash:[NSString stringWithFormat:@"%@-otherInfo", requestUrl]];}



第二种利用ASIHTTPRequest来缓存

ASIHTTPRequest,ASIDownloadCache  和 ASIWebPageRequest
   首先我得说,这确实是个很好的框架,使用起来确实很方便,但是对于缓存这个问题,好像也跟第二点提到的效果差不多,加载速度没有明显的提升,离线模式下也无法加载。这是实现的代码:

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. -(void)loadURL:(NSURL*)url  
  2. {  
  3.     ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];  
  4.     //ASIWebPageRequest *request= [ASIWebPageRequest requestWithURL:url];  
  5.     [request setDelegate:self];  
  6.     //[request setUrlReplacementMode:ASIReplaceExternalResourcesWithData];  
  7.     [request setDidFailSelector:@selector(webPageFetchFailed:)];  
  8.     [request setDidFinishSelector:@selector(webPageFetchSucceeded:)];  
  9.      //设置缓存  
  10.     [request setDownloadCache:[ASIDownloadCache sharedCache]];  
  11.     //[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];  
  12.     [request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];  
  13.     [request setDownloadDestinationPath:[[ASIDownloadCache sharedCache]pathToStoreCachedResponseDataForRequest:request]];  
  14.      [request startAsynchronous];  
  15. }  
  16.   
  17.   
  18.   
  19. - (void)webPageFetchFailed:(ASIHTTPRequest *)theRequest  
  20. {  
  21.     // Obviously you should handle the error properly...  
  22.     NSLog(@"%@",[theRequest error]);  
  23.     NSString *path = [[NSBundle mainBundle] pathForResource:@"error1.html" ofType:nil inDirectory:@"WebResources/Error"];  
  24.     NSURL  *url=[NSURL fileURLWithPath:path];    
  25.     [viewer loadRequest:[NSURLRequest requestWithURL:url]];  
  26. }  
  27.   
  28. - (void)webPageFetchSucceeded:(ASIHTTPRequest *)theRequest  
  29. {  
  30.     NSString *response = [NSString stringWithContentsOfFile:  
  31.                           [theRequest downloadDestinationPath] encoding:[theRequest responseEncoding] error:nil];  
  32.     // Note we're setting the baseURL to the url of the page we downloaded. This is important!  
  33.     [viewer loadHTMLString:response baseURL:[theRequest url]];  
  34.     //[viewer loadHTMLString:response baseURL:nil];  
  35. }  


0 0
原创粉丝点击