- 论坛徽章:
- 0
|
[转] 为UIWebView实现离线浏览2..........
接下来就可以实现cachedResponseForRequest:方法了。
我们得先判断是不是GET方法,因为其他方法不应该被缓存。还得判断是不是网络请求,例如http、https和ftp,因为连data协议等本地请求都会跑到这个方法里来…
Objective-c代码- static NSSet *supportSchemes;
-
- + (void)initialize {
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
- cacheDirectory = [[paths objectAtIndex:0] retain];
- supportSchemes = [[NSSet setWithObjects:@"http", @"https", @"ftp", nil] retain];
- }
-
- - (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];
- }
- //...
- }
- static NSSet *supportSchemes;
- + (void)initialize {
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
- cacheDirectory = [[paths objectAtIndex:0] retain];
- supportSchemes = [[NSSet setWithObjects:@"http", @"https", @"ftp", nil] retain];
- }
- - (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];
- }
- //...
- }
复制代码 因为没必要处理它们,所以直接交给父类的处理方法了,它会自行决定是否返回nil的。
接着判断是不是已经在cachedResponses里了,这样的话直接拿出来即可:
Objective-c代码- NSString *absoluteString = url.absoluteString;
- NSLog(@"%@", absoluteString);
- NSCachedURLResponse *cachedResponse = [cachedResponses objectForKey:absoluteString];
- if (cachedResponse) {
- NSLog(@"cached: %@", absoluteString);
- return cachedResponse;
- }
- NSString *absoluteString = url.absoluteString;
- NSLog(@"%@", absoluteString);
- NSCachedURLResponse *cachedResponse = [cachedResponses objectForKey:absoluteString];
- if (cachedResponse) {
- NSLog(@"cached: %@", absoluteString);
- return cachedResponse;
- }
复制代码 再查查responsesInfo里有没有,如果有的话,说明可以从磁盘获取:
Objective-c代码- NSDictionary *responseInfo = [responsesInfo objectForKey:absoluteString];
- if (responseInfo) {
- NSString *path = [cacheDirectory stringByAppendingString:[responseInfo objectForKey:@"filename"]];
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- if ([fileManager fileExistsAtPath:path]) {
- [fileManager release];
-
- NSData *data = [NSData dataWithContentsOfFile:path];
- NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[responseInfo objectForKey:@"MIMEType"] expectedContentLength:data.length textEncodingName:nil];
- cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
- [response release];
-
- [cachedResponses setObject:cachedResponse forKey:absoluteString];
- [cachedResponse release];
- NSLog(@"cached: %@", absoluteString);
- return cachedResponse;
- }
- [fileManager release];
- }
- NSDictionary *responseInfo = [responsesInfo objectForKey:absoluteString];
- if (responseInfo) {
- NSString *path = [cacheDirectory stringByAppendingString:[responseInfo objectForKey:@"filename"]];
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- if ([fileManager fileExistsAtPath:path]) {
- [fileManager release];
-
- NSData *data = [NSData dataWithContentsOfFile:path];
- NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[responseInfo objectForKey:@"MIMEType"] expectedContentLength:data.length textEncodingName:nil];
- cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
- [response release];
-
- [cachedResponses setObject:cachedResponse forKey:absoluteString];
- [cachedResponse release];
- NSLog(@"cached: %@", absoluteString);
- return cachedResponse;
- }
- [fileManager release];
- }
复制代码 这里的难点在于构造NSURLResponse和NSCachedURLResponse,不过对照下文档看看也就清楚了。如前文所说,我们还得把cachedResponse保存到cachedResponses里,避免它被提前释放。
接下来就说明缓存不存在了,需要我们自己发起一个请求。可恨的是NSURLResponse不能更改属性,所以还需要手动新建一个NSMutableURLRequest对象:
Objective-c代码- NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:request.timeoutInterval];
- newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
- newRequest.HTTPShouldHandleCookies = request.HTTPShouldHandleCookies;
- NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:request.timeoutInterval];
- newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
- newRequest.HTTPShouldHandleCookies = request.HTTPShouldHandleCookies;
复制代码 实际上NSMutableURLRequest还有一些其他的属性,不过并不太重要,所以我就只复制了这2个。
然后就可以用它来发起请求了。由于UIWebView就是在子线程调用cachedResponseForRequest:的,不用担心阻塞的问题,所以无需使用异步请求:
Objective-c代码- NSError *error = nil;
- NSURLResponse *response = nil;
- NSData *data = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];
- if (error) {
- NSLog(@"%@", error);
- NSLog(@"not cached: %@", absoluteString);
- return nil;
- }
- NSError *error = nil;
- NSURLResponse *response = nil;
- NSData *data = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];
- if (error) {
- NSLog(@"%@", error);
- NSLog(@"not cached: %@", absoluteString);
- return nil;
- }
复制代码 如果下载没出错的话,我们就能拿到data和response了,于是就能将其保存到磁盘了。保存的文件名必须是合法且独一无二的,所以我就用到了sha1算法。
Objective-c代码- NSString *filename = sha1([absoluteString UTF8String]);
- NSString *path = [cacheDirectory stringByAppendingString:filename];
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- [fileManager createFileAtPath:path contents:data attributes:nil];
- [fileManager release];
- NSString *filename = sha1([absoluteString UTF8String]);
- NSString *path = [cacheDirectory stringByAppendingString:filename];
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- [fileManager createFileAtPath:path contents:data attributes:nil];
- [fileManager release];
复制代码 接下来还得将文件信息保存到responsesInfo,并构造一个NSCachedURLResponse。
然而这里还有个陷阱,因为直接使用response对象会无效。我稍微研究了一下,发现它其实是个NSHTTPURLResponse对象,可能是它的allHeaderFields属性影响了缓存策略,导致不能重用。
不过这难不倒我们,直接像前面那样构造一个NSURLResponse对象就行了,这样就没有allHeaderFields属性了:
Objective-c代码- NSURLResponse *newResponse = [[NSURLResponse alloc] initWithURL:response.URL MIMEType:response.MIMEType expectedContentLength:data.length textEncodingName:nil];
- responseInfo = [NSDictionary dictionaryWithObjectsAndKeys:filename, @"filename", newResponse.MIMEType, @"MIMEType", nil];
- [responsesInfo setObject:responseInfo forKey:absoluteString];
- NSLog(@"saved: %@", absoluteString);
-
- cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse data:data];
- [newResponse release];
- [cachedResponses setObject:cachedResponse forKey:absoluteString];
- [cachedResponse release];
- return cachedResponse;
- NSURLResponse *newResponse = [[NSURLResponse alloc] initWithURL:response.URL MIMEType:response.MIMEType expectedContentLength:data.length textEncodingName:nil];
- responseInfo = [NSDictionary dictionaryWithObjectsAndKeys:filename, @"filename", newResponse.MIMEType, @"MIMEType", nil];
- [responsesInfo setObject:responseInfo forKey:absoluteString];
- NSLog(@"saved: %@", absoluteString);
- cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse data:data];
- [newResponse release];
- [cachedResponses setObject:cachedResponse forKey:absoluteString];
- [cachedResponse release];
- return cachedResponse;
复制代码 OK,现在终于大功告成了,打开WIFI然后启动这个程序,过一会就会提示缓存完毕了。然后关掉WIFI,尝试打开网页,你会发现网页能正常载入了。
而查看log,也能发现这确实是从我们的缓存中取出来的。
还不放心的话可以退出程序,这样内存缓存肯定就释放了。然后再次进入并打开网页,你会发现一切仍然正常~
摘自:http://www.keakon.net/2011/08/14/为UIWebView实现离线浏览
|
|