免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1831 | 回复: 1
打印 上一主题 下一主题

[转] 为UIWebView实现离线浏览2.......... [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-02-03 16:52 |只看该作者 |倒序浏览
[转] 为UIWebView实现离线浏览2..........








接下来就可以实现cachedResponseForRequest:方法了。
我们得先判断是不是GET方法,因为其他方法不应该被缓存。还得判断是不是网络请求,例如http、https和ftp,因为连data协议等本地请求都会跑到这个方法里来…
Objective-c代码
  1. static NSSet *supportSchemes;   
  2.   
  3. + (void)initialize {   
  4.     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);   
  5.     cacheDirectory = [[paths objectAtIndex:0] retain];   
  6.     supportSchemes = [[NSSet setWithObjects:@"http", @"https", @"ftp", nil] retain];   
  7. }   
  8.   
  9. - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {   
  10.     if ([request.HTTPMethod compare:@"GET"] != NSOrderedSame) {   
  11.         return [super cachedResponseForRequest:request];   
  12.     }   
  13.   
  14.     NSURL *url = request.URL;   
  15.     if (![supportSchemes containsObject:url.scheme]) {   
  16.         return [super cachedResponseForRequest:request];   
  17.     }   
  18.     //...   
  19. }  

  20. static NSSet *supportSchemes;

  21. + (void)initialize {
  22.     NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  23.     cacheDirectory = [[paths objectAtIndex:0] retain];
  24.     supportSchemes = [[NSSet setWithObjects:@"http", @"https", @"ftp", nil] retain];
  25. }

  26. - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
  27.     if ([request.HTTPMethod compare:@"GET"] != NSOrderedSame) {
  28.         return [super cachedResponseForRequest:request];
  29.     }

  30.     NSURL *url = request.URL;
  31.     if (![supportSchemes containsObject:url.scheme]) {
  32.         return [super cachedResponseForRequest:request];
  33.     }
  34.     //...
  35. }
复制代码
因为没必要处理它们,所以直接交给父类的处理方法了,它会自行决定是否返回nil的。

接着判断是不是已经在cachedResponses里了,这样的话直接拿出来即可:
Objective-c代码
  1. NSString *absoluteString = url.absoluteString;   
  2. NSLog(@"%@", absoluteString);   
  3. NSCachedURLResponse *cachedResponse = [cachedResponses objectForKey:absoluteString];   
  4. if (cachedResponse) {   
  5.     NSLog(@"cached: %@", absoluteString);   
  6.     return cachedResponse;   
  7. }  

  8. NSString *absoluteString = url.absoluteString;
  9. NSLog(@"%@", absoluteString);
  10. NSCachedURLResponse *cachedResponse = [cachedResponses objectForKey:absoluteString];
  11. if (cachedResponse) {
  12.     NSLog(@"cached: %@", absoluteString);
  13.     return cachedResponse;
  14. }
复制代码
再查查responsesInfo里有没有,如果有的话,说明可以从磁盘获取:
Objective-c代码
  1. NSDictionary *responseInfo = [responsesInfo objectForKey:absoluteString];   
  2. if (responseInfo) {   
  3.     NSString *path = [cacheDirectory stringByAppendingString:[responseInfo objectForKey:@"filename"]];   
  4.     NSFileManager *fileManager = [[NSFileManager alloc] init];   
  5.     if ([fileManager fileExistsAtPath:path]) {   
  6.         [fileManager release];   
  7.            
  8.         NSData *data = [NSData dataWithContentsOfFile:path];   
  9.         NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[responseInfo objectForKey:@"MIMEType"] expectedContentLength:data.length textEncodingName:nil];   
  10.         cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];   
  11.         [response release];   
  12.            
  13.         [cachedResponses setObject:cachedResponse forKey:absoluteString];   
  14.         [cachedResponse release];   
  15.         NSLog(@"cached: %@", absoluteString);   
  16.         return cachedResponse;   
  17.     }   
  18.     [fileManager release];   
  19. }  

  20. NSDictionary *responseInfo = [responsesInfo objectForKey:absoluteString];
  21. if (responseInfo) {
  22.     NSString *path = [cacheDirectory stringByAppendingString:[responseInfo objectForKey:@"filename"]];
  23.     NSFileManager *fileManager = [[NSFileManager alloc] init];
  24.     if ([fileManager fileExistsAtPath:path]) {
  25.         [fileManager release];
  26.         
  27.         NSData *data = [NSData dataWithContentsOfFile:path];
  28.         NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[responseInfo objectForKey:@"MIMEType"] expectedContentLength:data.length textEncodingName:nil];
  29.         cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
  30.         [response release];
  31.         
  32.         [cachedResponses setObject:cachedResponse forKey:absoluteString];
  33.         [cachedResponse release];
  34.         NSLog(@"cached: %@", absoluteString);
  35.         return cachedResponse;
  36.     }
  37.     [fileManager release];
  38. }
复制代码
这里的难点在于构造NSURLResponse和NSCachedURLResponse,不过对照下文档看看也就清楚了。如前文所说,我们还得把cachedResponse保存到cachedResponses里,避免它被提前释放。

接下来就说明缓存不存在了,需要我们自己发起一个请求。可恨的是NSURLResponse不能更改属性,所以还需要手动新建一个NSMutableURLRequest对象:
Objective-c代码
  1. NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:request.timeoutInterval];   
  2. newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;   
  3. newRequest.HTTPShouldHandleCookies = request.HTTPShouldHandleCookies;  

  4. NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:request.timeoutInterval];
  5. newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
  6. newRequest.HTTPShouldHandleCookies = request.HTTPShouldHandleCookies;
复制代码
实际上NSMutableURLRequest还有一些其他的属性,不过并不太重要,所以我就只复制了这2个。

然后就可以用它来发起请求了。由于UIWebView就是在子线程调用cachedResponseForRequest:的,不用担心阻塞的问题,所以无需使用异步请求:
Objective-c代码
  1. NSError *error = nil;   
  2. NSURLResponse *response = nil;   
  3. NSData *data = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];   
  4. if (error) {   
  5.     NSLog(@"%@", error);   
  6.     NSLog(@"not cached: %@", absoluteString);   
  7.     return nil;   
  8. }  

  9. NSError *error = nil;
  10. NSURLResponse *response = nil;
  11. NSData *data = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];
  12. if (error) {
  13.     NSLog(@"%@", error);
  14.     NSLog(@"not cached: %@", absoluteString);
  15.     return nil;
  16. }
复制代码
如果下载没出错的话,我们就能拿到data和response了,于是就能将其保存到磁盘了。保存的文件名必须是合法且独一无二的,所以我就用到了sha1算法。
Objective-c代码
  1. NSString *filename = sha1([absoluteString UTF8String]);   
  2. NSString *path = [cacheDirectory stringByAppendingString:filename];   
  3. NSFileManager *fileManager = [[NSFileManager alloc] init];   
  4. [fileManager createFileAtPath:path contents:data attributes:nil];   
  5. [fileManager release];  

  6. NSString *filename = sha1([absoluteString UTF8String]);
  7. NSString *path = [cacheDirectory stringByAppendingString:filename];
  8. NSFileManager *fileManager = [[NSFileManager alloc] init];
  9. [fileManager createFileAtPath:path contents:data attributes:nil];
  10. [fileManager release];
复制代码
接下来还得将文件信息保存到responsesInfo,并构造一个NSCachedURLResponse。
然而这里还有个陷阱,因为直接使用response对象会无效。我稍微研究了一下,发现它其实是个NSHTTPURLResponse对象,可能是它的allHeaderFields属性影响了缓存策略,导致不能重用。
不过这难不倒我们,直接像前面那样构造一个NSURLResponse对象就行了,这样就没有allHeaderFields属性了:
Objective-c代码
  1. NSURLResponse *newResponse = [[NSURLResponse alloc] initWithURL:response.URL MIMEType:response.MIMEType expectedContentLength:data.length textEncodingName:nil];   
  2. responseInfo = [NSDictionary dictionaryWithObjectsAndKeys:filename, @"filename", newResponse.MIMEType, @"MIMEType", nil];   
  3. [responsesInfo setObject:responseInfo forKey:absoluteString];   
  4. NSLog(@"saved: %@", absoluteString);   
  5.   
  6. cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse data:data];   
  7. [newResponse release];   
  8. [cachedResponses setObject:cachedResponse forKey:absoluteString];   
  9. [cachedResponse release];   
  10. return cachedResponse;  

  11. NSURLResponse *newResponse = [[NSURLResponse alloc] initWithURL:response.URL MIMEType:response.MIMEType expectedContentLength:data.length textEncodingName:nil];
  12. responseInfo = [NSDictionary dictionaryWithObjectsAndKeys:filename, @"filename", newResponse.MIMEType, @"MIMEType", nil];
  13. [responsesInfo setObject:responseInfo forKey:absoluteString];
  14. NSLog(@"saved: %@", absoluteString);

  15. cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse data:data];
  16. [newResponse release];
  17. [cachedResponses setObject:cachedResponse forKey:absoluteString];
  18. [cachedResponse release];
  19. return cachedResponse;
复制代码
OK,现在终于大功告成了,打开WIFI然后启动这个程序,过一会就会提示缓存完毕了。然后关掉WIFI,尝试打开网页,你会发现网页能正常载入了。
而查看log,也能发现这确实是从我们的缓存中取出来的。
还不放心的话可以退出程序,这样内存缓存肯定就释放了。然后再次进入并打开网页,你会发现一切仍然正常~

摘自:http://www.keakon.net/2011/08/14/为UIWebView实现离线浏览

论坛徽章:
0
2 [报告]
发表于 2012-02-03 16:52 |只看该作者
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP