加速加载WKWebView

时间:2015-04-18 23:16:35

标签: ios performance ios8 startup wkwebview

正如here所述,WKWebView有一个错误,即捆绑本地网页的应用必须将捆绑包复制到tmp目录中。我将捆绑包复制到tmp的代码是:

// 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.5s,总共~1s。)

有没有办法可以在iOS 8下加速(或完全避免)这个特定的代码片段?线程可以帮忙吗?

4 个答案:

答案 0 :(得分:5)

鉴于您的资产足够大,需要0.5秒才能复制,我的建议是采用稍微不同的方法来加快(明显)启动时间:

  1. 使用占位符div创建一个普通的旧HTML页面以加载资源(如果您愿意,可以在base64中包含小资源(例如加载指示符图像))
  2. 加载HTML后(可能在webView:didFinishNavigation:上),将副本启动到构建文件夹
  3. 复制完成后,使用javascript(使用evaluateJavaScript:completionHandler:
  4. 使用您的资源填充占位符div

    虽然我意识到这并没有直接解决这个问题,但我认为在这种情况下,方法的微小改变最好。

答案 1 :(得分:2)

注意:此技术适用于UIKit的UIWebView和AppKit的WebView,但不适用于新的WKWebView,它似乎忽略了网址加载系统。见评论。

使用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加载系统传输的任何内容。

答案 2 :(得分:1)

尝试XWebView而不复制文件。它包括一个微小的http服务器。您只需致电 [webview loadFileURL:allowingReadAccessToURL:]

答案 3 :(得分:1)

使这与0.5s的启动时间的其余部分并行肯定会有所帮助。 (只有当你在0.5s的其他启动时做的事情不依赖于正在加载的WebView或正在读取的文件时,这才有可能实现)

您将获得的最佳优化是减少&#34; build&#34;中的文件大小。目录。试试这些 -

  1. 如果您有任何内容,请缩小页面和javascript文件。 https://developers.google.com/speed/docs/insights/MinifyResources
  2. 如果目录大小​​非常大,请考虑将其压缩,将zip添加到应用程序,将zip复制到&#34; tmp&#34;并将其解压缩到内部&#34; tmp&#34;