UIWebView---UIWebView实现离线浏览

参考

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 plain copy 在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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值