Как сохранить содержимое в UIWebView для более быстрой загрузки при следующем запуске? - PullRequest
6 голосов
/ 28 августа 2009

Я знаю, что в iphone SDK недавно были введены некоторые классы кеширования, а также имеется запрос TTURLRequest из библиотеки Three20, который позволяет вам кэшировать запрос к URL. Однако, поскольку я загружаю веб-страницу в UIWebView, вызывая loadRequest UIWebView, эти методы на самом деле не применимы.

Есть идеи, как мне сохранить веб-страницу, чтобы при следующем запуске приложения мне не приходилось снова извлекать данные из Интернета для полной страницы? На самой странице уже есть некоторый механизм ajax, который автоматически обновляет свои части.

Ответы [ 5 ]

17 голосов
/ 18 марта 2010

Существует множество статей о том, как работает кэш UIWebView, и общее мнение таково, что даже если кажется, что некоторые механизмы работают нормально в MacOS X, те же подходы могут иметь любопытное поведение под iPhone.


ОДНАКО, Я делаю это, играя с глобальным кешем, к которому получают доступ любые NSURLConnection, UIWebView. А в моем случае это работает;).

Что вам нужно понять, так это глобальный поток:

  • ВЫ -> loadRequest на UIWebView
  • Это входит в NSURLCache, чтобы спросить "что-то кешируется для этого запроса?":
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request

После этого вот что я делаю, чтобы обработать кэш на диске, чтобы ускорить загрузку UIWebView:

  • Подкласс NSURLCache и переопределение управления получением над -(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request селектором
  • Переопределите этот селектор таким образом, чтобы, если на ФС ничего не было написано для этого запроса (без кэша), выполните запрос на своей стороне и сохраните содержимое на ФС. В противном случае верните то, что было ранее кэшировано.
  • Создайте экземпляр вашего подкласса и установите его в системе, чтобы он использовался вашим приложением

Теперь код:

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

Этот код заслуживает много очистки .. но главное должно быть там. У меня было много проблем, чтобы заставить это работать, надеюсь, это поможет.

3 голосов
/ 15 июня 2010

Я недавно нашел этот проект под github: http://github.com/rs/SDURLCache Подход такой же, как мой предыдущий ответ, описанный здесь Как сохранить содержимое в UIWebView для быстрой загрузки при следующем запуске? , но код выглядит более отточенным, поэтому, возможно, имеет смысл попробовать.

2 голосов
/ 18 декабря 2010
2 голосов
/ 17 марта 2010

Если на странице уже есть AJAX, почему бы не сохранить JavaScript / HTML в комплекте приложений для запуска, а не загружать его при первом запуске? Затем загрузите страницу с кодом, который дал Кори, ниже, и позвольте AJAX обрабатывать доступ к сети для обновленных частей страницы.

1 голос
/ 28 августа 2009

Вы можете сохранить HTML-код в каталоге документов и загрузить страницу непосредственно из каталога документов при запуске.

Чтобы сохранить содержимое веб-просмотра: Чтение содержимого HTML из UIWebView

Для загрузки:

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

    NSURLRequest* request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...