如何在UIWebView中保存内容以便在下次启动时加快加载速度?

时间:2009-08-27 20:12:12

标签: html ios iphone url uiwebview

我知道最近在iphone sdk中引入了一些缓存类,并且还有来自three20库的TTURLRequest,它允许您将请求缓存到URL。但是,因为我通过调用UIWebView的loadRequest在UIWebView中加载网页,所以这些技术并不适用。

任何想法如何保存网页,以便在下一个应用程序启动时,我不必再从网页上获取完整页面?页面本身已经有了一些自动更新部分的ajax机制。

5 个答案:

答案 0 :(得分:17)

关于UIWebView缓存的工作方式有很多文章,全球的感觉是,即使某些机制似乎在MacOS X下运行正常,但同样的方法可能会在iPhone下产生奇怪的行为。


但是,我是通过使用所包含的NSURLConnectionUIWebView访问的全局缓存来实现的。 就我而言,它有效;)。

您需要了解的是全球流程:

  • 你 - > loadRequest
  • 上的UIWebView
  • 进入NSURLCache询问“是否有针对此请求缓存的内容?”:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request

从那以后,这就是我在磁盘上处理缓存的方法,以加快UIWebView的加载速度:

  • NSURLCache进行子类化并覆盖对-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request选择器
  • 的get控件
  • 重新实现此选择器,如果FS上没有为此请求写入任何内容(没有缓存),则在您身边执行请求并将内容存储在FS上。否则,返回先前缓存的内容。
  • 创建子类的实例并将其设置为系统,以便应用程序使用它

现在代码:

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];

这段代码值得大量清理......但主要内容应该在那里。让这个工作变得很麻烦,希望这会有所帮助。

答案 1 :(得分:3)

我最近在github下发现了这个项目: http://github.com/rs/SDURLCache 这个方法与我之前在这里描述的How to save the content in UIWebView for faster loading on next launch?答案非常相似,但是代码看起来更加精致,所以尝试一下也许是有意义的。

答案 2 :(得分:2)

如果页面已经有AJAX,为什么不在应用程序包中存储JavaScript / HTML而不是在第一次启动时下载它?然后使用下面Corey给出的代码加载页面,让AJAX处理网页以获取页面的更新部分。

答案 3 :(得分:2)

答案 4 :(得分:1)

您可以将HTML保存在文档目录中,并在启动时直接从文档目录加载页面。

要保存webview内容: Reading HTML content from a UIWebView

加载:

    NSString* path = [[NSBundle mainBundle] pathForResource:@"about" ofType:@"html"];
    NSURL* url = [NSURL fileURLWithPath:path];

    NSURLRequest* request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];