2015-04-18 6 views
5

Как объяснено, here, WKWebView имеет ошибку, при которой приложения, которые связывают локальную веб-страницу, должны скопировать пакет в каталог tmp. Мой код для копирования сверток в tmp является:Ускорьте загрузку WKWebView

// Clear tmp directory 
NSArray* temporaryDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:NSTemporaryDirectory() error:NULL]; 
for (NSString *file in temporaryDirectory) { 
    [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), file] error:NULL]; 
} 

NSFileManager *fileManager = [NSFileManager defaultManager]; 
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"build"]; 
NSString *temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"build"]; 
NSError *error = nil; 

// Copy directory 
if(![fileManager copyItemAtPath:sourcePath toPath:temporaryPath error:&error]) { 
    [self logError:@"Could not copy directory" :error]; 
} 

Я рассчитал этот конкретный фрагмент кода, и он занимает 50% время запуска моего приложения! (~ 0,5 с в общей сложности ~ 1 с.)

Есть ли способ ускорить (или полностью исключить) этот фрагмент кода под iOS 8? Помогла ли резьба?

+0

Вы пробовали помещать этот код в фоновый режим? Прямо сейчас, похоже, он работает на основном потоке, что, безусловно, замедлит запуск. Общий 'NSFileManager' является потокобезопасным, поэтому вы не должны сталкиваться с какими-либо проблемами. –

+0

Копирование этих файлов находится на критическом пути моего приложения. Мое приложение - это «WKWebView», который нужно загружать как можно быстрее. – Randomblue

+0

Насколько большой пакет, который вы пытаетесь скопировать? – pteofil

ответ

1

Выполнение этой параллели с остальными моментами запуска 0.5 с. (Это было бы возможно только в том случае, если то, что вы делаете в оставшейся части 0,5 с момента запуска, не зависит от загружаемого WebView или считываемых файлов)

Лучшая оптимизация, которую вы получите, - это сокращение размер файлов внутри каталога «build». Попробуйте это -

  1. Минимизируйте свои страницы и файлы javascript, если они у вас есть. https://developers.google.com/speed/docs/insights/MinifyResources
  2. Если размер каталога значительно большим, рассмотрим сжать его, добавить почтовый индекс для приложения, копировать почтовый индекс в «TMP» и расстегивать его в «TMP»
+0

Ha, zipping - это крутая идея. – Randomblue

2

Примечание: Этот метод хорошо работает с UIKit-х UIWebView и App12it WebView, но не работает для нового WKWebView, который, как представляется, игнорирует систему загрузки URL-адресов. См. Комментарии.

Использовать NSURLProtocol для обработки локального файла, как если бы они были удаленными запросами. Полный пример см. В PandoraBoy под названием ResourceURLProtocol. Я пройду через немного упрощенную версию этого здесь. Мы будем читать http://.RESOURCE./path/to/file, как если бы это было <resources>/path/to/file.

У каждого NSURLProtocol будет задан вопрос, может ли он обрабатывать каждый запрос, который появляется в системе. Он должен ответить, может ли он в +canInitWithRequest:. Мы скажем, если хост .RESOURCE., тогда мы сможем его обработать. .RESOURCE. является незаконным DNS-именем, поэтому он не может конфликтовать с каким-либо реальным хостом (но это законное имя хоста для целей URL-адреса).

NSString *ResourceHost = @".RESOURCE."; 

+ (BOOL)canInitWithRequest:(NSURLRequest *)request { 
    return ([[[request URL] scheme] isEqualToString:@"http"] && 
      [[[request URL] host] isEqualToString:ResourceHost]); 
} 

Тогда нам нужно несколько методов бухгалтерского учета. Здесь ничего не видно.

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { 
    return request; 
} 

-(void)stopLoading { 
    return; 
} 

Теперь мы добираемся до мяса. startLoading - это то, где вы собираетесь делать все, что хотите сделать с запросом.

-(void)startLoading { 
    NSBundle *thisBundle = [NSBundle bundleForClass:[self class]]; 
    NSString *notifierPath = [[thisBundle resourcePath] stringByAppendingPathComponent:[[[self request] URL] path]]; 
    NSError *err; 
    NSData *data = [NSData dataWithContentsOfFile:notifierPath 
              options:NSUncachedRead // Assuming you only need to read this once 
              error:&err]; 
    if(data != nil) { 
     // Assuming you're only reading HTML. 
     // If you need other things, you'll need to work out the correct MIME type 
     NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[[self request] URL] 
                  MIMEType:@"text/html" 
               expectedContentLength:[data length] 
                textEncodingName:nil]; 

     // And we just pass it to the caller 
     [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed]; 
     [[self client] URLProtocol:self didLoadData:data]; 
     [[self client] URLProtocolDidFinishLoading:self]; 
    } else { 
     NSLog(@"BUG:Unable to load resource:%@:%@", notifierPath, [err description]); 
     [[self client] URLProtocol:self didFailWithError:err]; 
    } 
} 

Я также найти немного ResourceURL обертку полезным:

@implementation ResourceURL 
+ (ResourceURL*) resourceURLWithPath:(NSString *)path { 
    return [[[NSURL alloc] initWithScheme:@"http" 
            host:ResourceHost 
            path:path] autorelease]; 
}  
@end 

Чтобы использовать его, вам просто нужно сначала зарегистрировать свой обработчик протокола:

[NSURLProtocol registerClass:[ResourceURLProtocol class]]; 

Тогда вы можете создать " URL ресурса "и загрузить его:

ResourceURL *resource = [ResourceURL resourceURLWithPath:...]; 
[webView loadRequest:[NSURLRequest requestWithURL:resource]]; 

Для получения дополнительной информации о NSURLProtocol, а также более сложного примера кэширования см. Drop-in Offline Caching for UIWebView (and NSURLProtocol).

PandoraBoy полон NSURLProtocol примеров (ищите все классы с Protocol в своих именах). Вы можете захватывать, отслеживать, перенаправлять или манипулировать практически всем, что происходит через систему загрузки URL таким образом.

+0

Я могу получить 'canInitWithRequest', чтобы вернуть' YES', но 'startLoading' никогда не вызывается. Есть идеи по этому поводу? – Randomblue

+0

Хмм, ты это видел? http://stackoverflow.com/a/24208322/707381 – Randomblue

+0

И это ... http://stackoverflow.com/a/24982211/707381 – Randomblue

5

Учитывая, что ваши активы достаточно велики, чтобы принять 0,5с, чтобы копировать, мое предложение было бы принять несколько иной подход к ускорят (видимую) время запуска:

  1. Создать обычный старый HTML страницу с заполнителем div ами для загрузки активов (если вы хотите, вы можете включать в себя небольшие активы (например, индикатор загрузки изображений) в base64)
  2. После того, что HTML нагрузки (возможно, на webView:didFinishNavigation:), запустить копию в папку сборки
  3. Как только копия завершится, заполните заполняющие divs вашими активами u петь javascript (с evaluateJavaScript:completionHandler:)

Хотя я понимаю, что это прямо не затрагивает вопрос, я думаю, что в этом случае было бы лучше изменить подход.

+0

Это очень интересно, спасибо! – Randomblue

Смежные вопросы