如何在UIWebView中保存内容以便在下次启动时加载更快?
我知道最近在iphone sdk中引入了一些caching类,并且还有一个来自three20库的TTURLRequest,允许你caching一个请求到一个URL。 但是,因为我通过调用UIWebView的loadRequest在UIWebView中加载网页,所以这些技术并不适用。
任何想法如何保存一个网页,以便在下一次应用程序启动时,我不必从整个页面再次从networking上获取? 页面本身已经有了一些自动更新自身部分的Ajax机制。
有很多关于UIWebView的caching方式的文章,全球的感觉是,即使某些机制在MacOS X下似乎可以正常工作,相同的方法也可能在iPhone下有好奇的行为。
但是,我通过玩任何NSURLConnection
,包括UIWebView
访问全局caching来做到这一点。 而在我的情况下,它的工作原理;)。
你需要了解的是全球stream量:
- 你 – >在
UIWebView
loadRequest
- 这进入
NSURLCache
询问“有什么东西caching这个请求?”:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
从这里,我做了什么来处理磁盘上的caching,在我这边,以加快UIWebView的负载:
-
NSURLCache
并覆盖对-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
select器的get控制 - 以这样的方式重新实现这个select器,如果这个请求没有写入任何内容(没有caching),那么在你身边做请求并把内容存储在FS上。 否则,返回以前caching的内容。
- 创build一个你的子类的实例,并将其设置为系统,以便它被应用程序使用
现在代码:
MyCache.h
@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
并在你的应用程序中使用它:
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];
这个代码值得大量的清理..但主要的东西应该在那里。 我有很多麻烦得到这个工作,希望这有助于。
我最近发现这个项目在github下: http : //github.com/rs/SDURLCache这个方法和我在前面描述的答案非常相似如何在UIWebView中保存内容以便在下次启动时加载更快? ,但代码看起来更加精美,所以尝试一下也许是有道理的。
如果页面已经存在AJAX,那么为什么不把JavaScript / HTML存储在应用程序包中,而是先启动而不是下载呢? 然后使用下面的代码Corey给出的页面加载页面,并让AJAX处理页面的更新部分。
看看: http : //allseeing-i.com/ASIHTTPRequest/ASIWebPageRequest
您可以将HTML保存在文档目录中,并在启动时直接从文档目录加载页面。
保存webview内容: 从UIWebView中读取HTML内容
加载:
NSString* path = [[NSBundle mainBundle] pathForResource:@"about" ofType:@"html"]; NSURL* url = [NSURL fileURLWithPath:path]; NSURLRequest* request = [NSURLRequest requestWithURL:url]; [webView loadRequest:request];