是否可以在 webview 上显示之前预加载 html

Posted

技术标签:

【中文标题】是否可以在 webview 上显示之前预加载 html【英文标题】:is it possible to preload html before displaying on webview 【发布时间】:2017-04-23 05:41:06 【问题描述】:

我已经搜索了整个互联网,但找不到解决方案,所以决定问专家这是否可能。

我有一个博客,我正在为其开发 ios 应用程序,但加载内容需要花费太多时间。

那么是否可以在用户打开文章之前预加载所有文章的 html(不是图片),就像 FB 即时文章一样。 FB即时文章是预加载和缓存的,当用户打开它时,它会在Flash中打开,除了图像,当用户打开它时会下载。

目前我正在使用 uiwebview,我打算用 wkwebview 替换它。

提前感谢您的帮助。

调用缓存

CachedArticles.shared.cacheArticles(self.articles)  success 
    print("cache creation finished, success: \(success)")

调用 webview

tableView.deselectRow(at: indexPath, animated: true)
let webVC = storyboard!.instantiateViewController(withIdentifier:"NewsReaderViewController") as! NewsReaderViewController
webVC.article = self.articles?[indexPath.row]
navigationController?.pushViewController(webVC, animated: true)

缓存类

import UIKit

class CachedArticles: NSObject 

static let shared: CachedArticles = CachedArticles()

let cachePath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first!

func cacheArticles(_ articles: [ArticalClass]?, completion: @escaping (Bool)->() ) 

    guard let articles = articles else  return 

    DispatchQueue.global().async 
        DispatchQueue.main.async 
            UIApplication.shared.isNetworkActivityIndicatorVisible = true
        

        var success = true

        for article in articles 
            guard let path = article.url, let url = URL(string: path), self.webArchive(forHeadline: article.headline!.replacingOccurrences(of: " ", with: "")) == nil else  continue 

            print("caching article: \(article)")
            do 
                let data = try Data(contentsOf: url)
                let url = self.cacheFileURL(forHeadline: article.headline!)
                STWebArchiver().archiveHTMLData(data, textEncoding: "UTF-8", baseURL: url, completionBlock:  data in
                    (data! as NSData).write(toFile: url.path, atomically: true)
                )
             catch let error 
                success = false
                print("Error: \(error)")
            
        

        DispatchQueue.main.async 
            UIApplication.shared.isNetworkActivityIndicatorVisible = false
            completion(success)
        
    


func webArchive(forHeadline: String?) -> URL? 
    let url = cacheFileURL(forHeadline: forHeadline!)
    return FileManager.default.fileExists(atPath: url.path) ? url : nil


private func cacheFileURL(forHeadline: String) -> URL 
    let url = URL(fileURLWithPath: self.cachePath).appendingPathComponent(forHeadline.replacingOccurrences(of: " ", with: "")+".webarchive")
    return url



【问题讨论】:

【参考方案1】:

您可以在预览时通过HTTP请求获取html文件并将其保存到缓存中,并在您真正进入时检查它是否在缓存中。

================================================ ==

4.24 更新 有两个因素

首先,我不知道 'articles' 数组是如何生成的,以及它是否包含 src url。

其次,我查看了 STWebArchiver 代码并找到了它:

- (void)archiveHTMLData:(NSData *)aData
       textEncoding:(NSString *)anEncoding
            baseURL:(NSURL *)anURL
    completionBlock:(void (^)(NSData *))completion 
htmlDocPtr doc = htmlParseDoc((xmlChar *)[aData bytes], [anEncoding UTF8String]);
NSArray *pathsForImagesAndScripts = [self valueForAttributeName:@"src" withEvaluatingXPath:@"//script[@src]|//img[@src]" inDocument:doc];
NSArray *pathsForStylesheets = [self valueForAttributeName:@"href" withEvaluatingXPath:@"//link[@rel='stylesheet'][@href]" inDocument:doc];
NSArray *resourcesPaths = [pathsForImagesAndScripts arrayByAddingObjectsFromArray:pathsForStylesheets];
NSArray *resourceUrls = [self absoluteURLsForPaths:resourcesPaths baseURL:anURL];
dispatch_async(dispatch_queue_create("Downloads", 0), ^
    NSMutableDictionary *resources = [NSMutableDictionary dictionary];
    dispatch_apply([resourceUrls count], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^(size_t i) 
        NSURL *url = [resourceUrls objectAtIndex:i];
        NSString *urlString = [url absoluteString];
        BOOL unfetched = NO;
        @synchronized (resources) 
            unfetched = ![resources objectForKey:urlString];
            if (unfetched) 
                [resources setObject:[NSNull null] forKey:urlString];
            
        
        if (unfetched) 
            NSURLResponse *response;
            NSError *error;
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            NSData *data = [NSURLConnection sendSynchronousRequest:request
                                                 returningResponse:&response
                                                             error:&error];
            NSMutableDictionary *resourceArchive = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                    urlString, @"WebResourceURL",
                                                    [response MIMEType], @"WebResourceMIMEType",
                                                    data, @"WebResourceData", nil];
            if ([response textEncodingName]) 
                [resourceArchive setObject:[response textEncodingName] forKey:@"WebResourceTextEncodingName"];
            
            @synchronized (resources) 
                [resources setObject:resourceArchive forKey:urlString];
            
        
    );
    NSMutableDictionary *archiveSource = [NSMutableDictionary dictionaryWithObject:[resources allValues] forKey:@"WebSubresources"];
    NSMutableDictionary *mainResource = [NSMutableDictionary dictionary];
    [mainResource setObject:aData forKey:@"WebResourceData"];
    [mainResource setObject:@"" forKey:@"WebResourceFrameName"];
    [mainResource setObject:@"text/html" forKey:@"WebResourceMIMEType"];
    [mainResource setObject:anEncoding forKey:@"WebResourceTextEncodingName"];
    [mainResource setObject:[anURL absoluteString] forKey:@"WebResourceURL"];
    [archiveSource setObject:mainResource forKey:@"WebMainResource"];
    NSData *webArchive = [NSPropertyListSerialization dataFromPropertyList:archiveSource
                                                                    format:NSPropertyListBinaryFormat_v1_0
                                                          errorDescription:NULL];
    completion(webArchive);
);
xmlFreeDoc(doc);

这意味着 STWebArchiver 不仅缓存纯 HTML 文件,还缓存请求的资源。

【讨论】:

我厌倦了,但它也正在获取/下载图像,这非常消耗数据。 非常感谢,我会查看它是否有帮助。还有文章内容的 src url 我也在避免图像缓存,因为它会占用大量用户的带宽。【参考方案2】:

您可以通过在将它们添加到视图层次结构并启动它们的请求之前预先创建它们来执行“贪婪”的 webview 方法。假设每个 webview 实例将显示一个不导航到其他 html 页面的页面。 appDidFinishLaunching 将是进行设置的地方。

【讨论】:

不幸的是,它可能会导航到其他 html 页面。

以上是关于是否可以在 webview 上显示之前预加载 html的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 中预加载网页(使用 WebView?)

mui预加载

在android上加载webview后没有显示插页式广告?

(源码)解决Android的WebView加载失败(404,500),显示的自定义视图

Android WebView 未从缓存中加载第二页

预加载多个本地WebView