Как сохранить контент в UIWebView для более быстрой загрузки при следующем запуске?
Я знаю, что в iphone SDK недавно были введены некоторые классы кэширования, а также есть TTURLRequest из библиотеки Three20, который позволяет вам кэшировать запрос к URL. Однако, поскольку я загружаю веб-страницу в UIWebView, вызывая loadRequest UIWebView, эти методы на самом деле не применимы.
Любые идеи, как я могу сохранить веб-страницу, чтобы при следующем запуске приложения мне не пришлось снова загружать данные из Интернета для полной страницы? На самой странице уже есть некоторый механизм ajax, который автоматически обновляет части себя.
5 ответов
Существует множество статей о том, как работает кэш UIWebView, и общее мнение таково, что даже если кажется, что некоторые механизмы работают нормально в MacOS X, те же подходы могут иметь любопытное поведение под iPhone.
ОДНАКО, я делаю это, играя с глобальным кешем, к которому NSURLConnection
, UIWebView
включен. И в моем случае это работает;).
Что вам нужно понять, так это глобальный поток:
- ВЫ ->
loadRequest
наUIWebView
- Это входит в
NSURLCache
спросить "что-то кешируется для этого запроса?"
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
Исходя из этого, вот что я делаю для обработки кэша на диске, для того чтобы ускорить загрузку UIWebView:
- Подкласс
NSURLCache
и переопределить получить контроль над-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
селектор - Переопределите этот селектор таким образом, чтобы, если на 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];
Этот код заслуживает много очистки.. но главное должно быть там. У меня было много проблем, чтобы заставить это работать, надеюсь, это поможет.
Недавно я нашел этот проект в github: http://github.com/rs/SDURLCache Этот подход почти такой же, как мой предыдущий ответ, описанный здесь. Как сохранить содержимое в UIWebView для более быстрой загрузки при следующем запуске?, но код выглядит более отточенным, так что, возможно, имеет смысл попробовать.
Взгляните на: http://allseeing-i.com/ASIHTTPRequest/ASIWebPageRequest
Если на странице уже есть AJAX, почему бы не сохранить JavaScript/HTML в комплекте приложений для запуска, а не загружать его при первом запуске? Затем загрузите страницу с кодом, который дал Кори, ниже, и позвольте AJAX обрабатывать доступ к сети для обновленных частей страницы.
Вы можете сохранить HTML в каталоге документов и загрузить страницу прямо из каталога документов при запуске.
Чтобы сохранить содержимое веб-просмотра: Чтение содержимого HTML из UIWebView
Загрузить:
NSString* path = [[NSBundle mainBundle] pathForResource:@"about" ofType:@"html"];
NSURL* url = [NSURL fileURLWithPath:path];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];