How to save the content in UIWebView for faster loading on next launch?(如何在 UIWebView 中保存内容以便在下次启动时更快地加载?)
问题描述
我知道最近在 iphone sdk 中引入了一些缓存类,并且还有一个来自three20 的库的 TTURLRequest,可以让您缓存对 URL 的请求.但是,因为我是通过调用 UIWebView 的 loadRequest 在 UIWebView 中加载网页,所以这些技术并不真正适用.
I know that there are some caching classes introduced in the iphone sdk recently, and there is also a TTURLRequest from three20's library that allows you to cache a request to a URL. However, because I am loading the web page in UIWebView by calling UIWebView's loadRequest, those techniques are not really applicable.
有什么想法可以保存网页,以便在下一次应用启动时,我不必再次从网络上获取整个页面?页面本身已经有一些自动更新自身部分的 ajax 机制.
Any ideas how I can save a web page so that on next app launch, I don't have to fetch from the web again for the full page? The page itself already have some ajax mechanism that updates parts of itself automatically.
推荐答案
关于UIWebView的缓存工作方式的文章有一大堆,总体感觉是,即使某些机制在MacOS X下似乎可以正常工作,相同的方法在 iPhone 下可能会有奇怪的行为.
There are a bunch of articles about the way the cache of the UIWebView works and the global feeling is that even if some mechanisms seems to work OK under MacOS X, the same approaches may have curious behavior under iPhone.
但是,我正在通过使用任何 NSURLConnection
访问的全局缓存来做到这一点,包括 UIWebView
.就我而言,它有效;).
HOWEVER, I'm doing it by playing with the global cache that is accessed by any NSURLConnection
, UIWebView
included. And in my case, it works ;).
你需要了解的是全局流:
What you need to understand is the global flow:
- 你 ->
loadRequest
在UIWebView
- 这会进入
NSURLCache
以询问是否有为此请求缓存的内容?":
- YOU ->
loadRequest
on aUIWebView
- This goes into
NSURLCache
to ask "is there something cached for this request?":
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
从那以后,这就是我在磁盘上处理缓存的方法,在我这边,以加快 UIWebView 的负载:
From that, here's what I do to handle the cache on the disk, on my side, to speed up the load of a UIWebView:
- 继承
NSURLCache
并覆盖对-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
选择器的控制权 - 重新实现此选择器,如果 FS 上没有为此请求写入任何内容(无缓存),则在您这边执行请求并将内容存储在 FS 上.否则,返回之前缓存的内容.
- 创建子类的实例并将其设置为系统,以便您的应用程序使用它
- Subclass the
NSURLCache
and override the get control over the-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
selector - Reimplement this selector in such a way that if nothing has been written on the FS for this request (no cache), then do the request on your side and store the content on FS. Otherwise, return what has been previously cached.
- Create an instance of your subclass and set it to the system so that it is used by your application
现在是代码:
@interface MyCache : NSURLCache {
}
@end
MyCache.m
@implementation MyCache
-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog(@"CACHE REQUEST S%@", request);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:@"/"];
if (tokens==nil) {
NSLog(@"ignoring cache for %@", request);
return nil;
}
NSString* pathWithoutRessourceName=@"";
for (int i=0; i<[tokens count]-1; i++) {
pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:@"%@%@", [tokens objectAtIndex:i], @"/"]];
}
NSString* absolutePath = [NSString stringWithFormat:@"%@%@", documentsDirectory, pathWithoutRessourceName];
NSString* absolutePathWithRessourceName = [NSString stringWithFormat:@"%@%@", documentsDirectory, request.URL.relativePath];
NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:@""];
NSCachedURLResponse* cacheResponse = nil;
//we're only caching .png, .js, .cgz, .jgz
if (
[ressourceName rangeOfString:@".png"].location!=NSNotFound ||
[ressourceName rangeOfString:@".js"].location!=NSNotFound ||
[ressourceName rangeOfString:@".cgz"].location!=NSNotFound ||
[ressourceName rangeOfString:@".jgz"].location!=NSNotFound) {
NSString* storagePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, request.URL.relativePath];
//this ressource is candidate for cache.
NSData* content;
NSError* error = nil;
//is it already cached ?
if ([[NSFileManager defaultManager] fileExistsAtPath:storagePath]) {
//NSLog(@"CACHE FOUND for %@", request.URL.relativePath);
content = [[NSData dataWithContentsOfFile:storagePath] retain];
NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"" expectedContentLength:[content length] textEncodingName:nil];
cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
} else {
//trick here : if no cache, populate it asynchronously and return nil
[NSThread detachNewThreadSelector:@selector(populateCacheFor:) toTarget:self withObject:request];
}
} else {
NSLog(@"ignoring cache for %@", request);
}
return cacheResponse;
}
-(void)populateCacheFor:(NSURLRequest*)request {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//NSLog(@"PATH S%@", paths);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:@"/"];
NSString* pathWithoutRessourceName=@"";
for (int i=0; i<[tokens count]-1; i++) {
pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:@"%@%@", [tokens objectAtIndex:i], @"/"]];
}
NSString* absolutePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, pathWithoutRessourceName];
//NSString* absolutePathWithRessourceName = [NSString stringWithFormat:@"%@%@", documentsDirectory, request.URL.relativePath];
//NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:@""];
NSString* storagePath = [NSString stringWithFormat:@"%@/myCache%@", documentsDirectory, request.URL.relativePath];
NSData* content;
NSError* error = nil;
NSCachedURLResponse* cacheResponse = nil;
NSLog(@"NO CACHE FOUND for %@", request.URL);
//NSLog(@"retrieving content (timeout=%f) for %@ ...", [request timeoutInterval], request.URL);
content = [NSData dataWithContentsOfURL:request.URL options:1 error:&error];
//NSLog(@"content retrieved for %@ / error:%@", request.URL, error);
if (error!=nil) {
NSLog(@"ERROR %@ info:%@", error, error.userInfo);
NSLog(@"Cache not populated for %@", request.URL);
} else {
NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"" expectedContentLength:[content length] textEncodingName:nil];
cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
//the store is invoked automatically.
[[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
BOOL ok;// = [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
ok = [content writeToFile:storagePath atomically:YES];
NSLog(@"Caching %@ : %@", storagePath , ok?@"OK":@"KO");
}
[pool release];
}
@end
以及在您的应用程序中使用它:
And the use of it in your application:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* diskCachePath = [NSString stringWithFormat:@"%@/%@", documentsDirectory, @"myCache"];
NSError* error;
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:&error];
MyCache* cacheMngr = [[MyCache alloc] initWithMemoryCapacity:10000 diskCapacity:100000000 diskPath:diskCachePath];
[NSURLCache setSharedURLCache:cacheMngr];
这段代码值得大量清理.但主要的东西应该在那里.我在完成这项工作时遇到了很多麻烦,希望这会有所帮助.
This code deserves a lot of cleanup.. but the main things should be in there. I had a lot of trouble to get this working, hope this helps.
这篇关于如何在 UIWebView 中保存内容以便在下次启动时更快地加载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何在 UIWebView 中保存内容以便在下次启动时更快地加载?
基础教程推荐
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01