问题 需要UIWebView中的内容才能快速显示


我的应用程序的一部分缓存网页以供离线查看。为此,我保存从站点获取的HTML并重写img urls以指向本地存储上的文件。当我将html加载到UIWebView中时,它按预期加载图像,一切都很好。我也以这种方式缓存样式表。

问题是,当我将手机置于飞行模式时,加载此缓存的html会导致UIWebView显示一个空白屏幕,并在显示页面之前暂停一段时间。我发现它是由Web视图试图获取的原始HTML文档引用的非缓存URL引起的。这些其他URL包括缓存样式表中的图像,iframe中的内容以及打开连接以获取其他资源的javascript。当UIWebView尝试获取这些资源时会发生暂停,并且仅在所有这些其他提取超时后才会显示该网页。

我的问题是,如何让UIWebView显示我立即缓存的内容?以下是我的想法:

  • 写更多代码来缓存这些其他引用。这可能需要更多的代码来捕获所有边缘情况等,特别是必须解析Javascript以查看在加载页面后它加载了什么
  • 强制UIWebView立即超时,因此没有暂停。我还没弄明白怎么做。
  • 以某种方式获取已经加载的内容,即使外部引用还没有完成提取
  • 剥离所有脚本的代码,链接标签和iframe以“擦除”外部引用。我试过这个,但对于一些网站,结果页面严重搞砸了

有人能帮我一下吗?我一直在努力解决这个问题,而且我的想法已经不多了。


8611
2017-08-07 18:54


起源



答案:


编辑:只是指出这个问题及其答案已有4年了。我正在聊聊下面的iOS 3.x.我可以很容易地想象事情在此期间发生了变化 - 哎呀,单独的设备比iPhone 3G更快。因人而异。 :)

我一直在看这个过去,而不是实际上实现任何东西,只是谷歌搜索。总的来说,使用DontLoad在iPhone上执行此操作非常困难/不可能。即使在三月份,也有报道说它可以在Simulator中运行,但不能在设备上运行: http://discussions.apple.com/message.jspa?messageID=9245292

我只是猜测,但基于其他StackOverflow问题(例如 这个) 我想也许设备上的缓存在某种程度上比它的Mac和Safari同类产品更加有限。

Apple论坛上的其他人在询问UIWebView和NSURLCache时报告了粗鲁的工程师的经验,在bug报告中,工程师说它应该有效,但实际的开发人员说它没有。 http://discussions.apple.com/thread.jspa?threadID=1588010 (7月29日至8月20日)

可能会找到一些解决方案 这里。考虑到[UIImage imageWithData:...],David描述了“用于iPhone的异步缓存图像下载器的实现”的一些代码。

现在我在cocoa-dev邮件列表上发现了这个8月10日的电子邮件:

2009年8月10日下午1:38,Mike Manzano写道:

有没有人能够成功     在iPhone上获取URL加载系统     要注意自定义版本     NSURLCache?在我看来,它是     被忽略了。更多信息:

[此页面的链接;删除。]

(见第二个“答案”     页)。

如果我是的话,只需要某种线索     做错了或者我的代码是     被忽略了

是的,我有它的工作 - 只是   需要制作自己的缓存   实例并明确设置   共享缓存到该新实例。

但它绝对有效 - 一次   实施,我的“设置表视图   细胞的图像从网络图像“   很好地工作(没有它滚动   最痛苦的事)。

Glenn Andreas gandreas @ xxxxxxxxxxxx    http://www.gandreas.com/ 邪恶的乐趣!   疯狂,坏,并且知道危险

上面的电子邮件可以在 http://www.cocoabuilder.com/archive/message/cocoa/2009/8/10/242484 (没有进一步的答复)

可以进一步证明NSURLCache可以工作 iCab Blog:iPhone上的UIWebView URL过滤 (2009年8月18日)

这里的任何人都可能应该关注Apple的URLCache示例应用程序,因为它可能与他们的工作相关: https://developer.apple.com/iphone/library/samplecode/URLCache/index.html 除了刚刚查看它之外,它说“此应用程序不使用NSURLCache磁盘或内存缓存,因此我们的缓存策略是通过从其源加载数据来满足请求。”和用途 cachePolicy:NSURLRequestReloadIgnoringLocalCacheData。所以这不像我想的那么重要,为什么 这个人 打电话给NSURLCache是​​一个笑话。

因此,看起来NSURLCache并不像我开始这篇文章那样棘手或不可能。喜欢找到一种有效的方法,同时研究它不起作用的原因。我仍然无法相信“iphone”和“NSURLRequestReturnCacheDataDontLoad”只有25个Google搜索结果...这个答案几乎包含了每一个。而且我很高兴我写了它以便稍后可以参考;-)


10
2017-08-28 05:41



感谢您花时间总结一下。 - jm.


答案:


编辑:只是指出这个问题及其答案已有4年了。我正在聊聊下面的iOS 3.x.我可以很容易地想象事情在此期间发生了变化 - 哎呀,单独的设备比iPhone 3G更快。因人而异。 :)

我一直在看这个过去,而不是实际上实现任何东西,只是谷歌搜索。总的来说,使用DontLoad在iPhone上执行此操作非常困难/不可能。即使在三月份,也有报道说它可以在Simulator中运行,但不能在设备上运行: http://discussions.apple.com/message.jspa?messageID=9245292

我只是猜测,但基于其他StackOverflow问题(例如 这个) 我想也许设备上的缓存在某种程度上比它的Mac和Safari同类产品更加有限。

Apple论坛上的其他人在询问UIWebView和NSURLCache时报告了粗鲁的工程师的经验,在bug报告中,工程师说它应该有效,但实际的开发人员说它没有。 http://discussions.apple.com/thread.jspa?threadID=1588010 (7月29日至8月20日)

可能会找到一些解决方案 这里。考虑到[UIImage imageWithData:...],David描述了“用于iPhone的异步缓存图像下载器的实现”的一些代码。

现在我在cocoa-dev邮件列表上发现了这个8月10日的电子邮件:

2009年8月10日下午1:38,Mike Manzano写道:

有没有人能够成功     在iPhone上获取URL加载系统     要注意自定义版本     NSURLCache?在我看来,它是     被忽略了。更多信息:

[此页面的链接;删除。]

(见第二个“答案”     页)。

如果我是的话,只需要某种线索     做错了或者我的代码是     被忽略了

是的,我有它的工作 - 只是   需要制作自己的缓存   实例并明确设置   共享缓存到该新实例。

但它绝对有效 - 一次   实施,我的“设置表视图   细胞的图像从网络图像“   很好地工作(没有它滚动   最痛苦的事)。

Glenn Andreas gandreas @ xxxxxxxxxxxx    http://www.gandreas.com/ 邪恶的乐趣!   疯狂,坏,并且知道危险

上面的电子邮件可以在 http://www.cocoabuilder.com/archive/message/cocoa/2009/8/10/242484 (没有进一步的答复)

可以进一步证明NSURLCache可以工作 iCab Blog:iPhone上的UIWebView URL过滤 (2009年8月18日)

这里的任何人都可能应该关注Apple的URLCache示例应用程序,因为它可能与他们的工作相关: https://developer.apple.com/iphone/library/samplecode/URLCache/index.html 除了刚刚查看它之外,它说“此应用程序不使用NSURLCache磁盘或内存缓存,因此我们的缓存策略是通过从其源加载数据来满足请求。”和用途 cachePolicy:NSURLRequestReloadIgnoringLocalCacheData。所以这不像我想的那么重要,为什么 这个人 打电话给NSURLCache是​​一个笑话。

因此,看起来NSURLCache并不像我开始这篇文章那样棘手或不可能。喜欢找到一种有效的方法,同时研究它不起作用的原因。我仍然无法相信“iphone”和“NSURLRequestReturnCacheDataDontLoad”只有25个Google搜索结果...这个答案几乎包含了每一个。而且我很高兴我写了它以便稍后可以参考;-)


10
2017-08-28 05:41



感谢您花时间总结一下。 - jm.


生成一个 NSURLRequest 同 +requestWithURL:cachePolicy:timeoutInterval:。将缓存策略设置为 NSURLRequestReturnCacheDataDontLoad。使用将请求加载到webview中 -loadRequest:。完整的文档 这里


1
2017-08-07 19:08



嗨,我的回复评论太长了,所以我把它放在下面的答案中。 - leftspin


谢谢你的领导!它让我松了一口气。不幸的是,我似乎陷入了一堆障碍。

问题是,如果我使用NSURLRequestReturnCacheDataDontLoad,我认为我正在加载的资源必须已经在缓存中。

为了把它们放在缓存中,我尝试了这个:

    NSURLRequest *request =     [NSURLRequest 
                            requestWithURL:cachedURL 
                            cachePolicy:NSURLRequestReloadIgnoringLocalCacheData 
                            timeoutInterval:60.0] ;

    NSURLResponse *responseToCache = 
                        [[NSURLResponse alloc]
                            initWithURL:[request URL] 
                            MIMEType:@"text/html" 
                            expectedContentLength:[dataAtURL length] 
                            textEncodingName:nil] ;

    NSCachedURLResponse *cachedResponse = 
                        [[NSCachedURLResponse alloc]
                            initWithResponse:responseToCache data:dataAtURL
                            userInfo:nil storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] ;

        // Store it
    [[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:request] ;

    [responseToCache release] ;
    [cachedResponse release] ;

    NSLog(  @"*** request %@, cache=%@", request ,[[NSURLCache sharedURLCache] 
            cachedResponseForRequest:request]                           ) ;

我从网上的其他地方得到了这个代码,但我认为虽然它适用于Mac,但它不适用于我的目标平台iPhone。

它似乎没有将项插入缓存; NSLog为“cache =%@”打印null。

然后,我尝试重载NSURLCache:

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
    {
    NSLog( @"FileFriendlyURLCache asked from request %lx of type %@ for data at URL: %@" , 
        request , [request class] , [[request URL] absoluteString] ) ;

    NSCachedURLResponse *result = nil ;

    if( [[[request URL] absoluteString] hasPrefix:@"file://"] )
        {
        NSLog( @"\tFulfilling from cache" ) ;

        NSError *error = nil ;
        NSData *dataAtURL = [NSData dataWithContentsOfURL:[request URL] 
                        options:0 error:&error] ;
        if( error )
            NSLog( @"FileFriendlyURLCache encountered an error while loading %@: %@" ,
                [request URL] , error ) ;

        NSURLResponse *reponse = [[NSURLResponse alloc]
                                    initWithURL:[request URL] 
                                    MIMEType:@"text/html" 
                                    expectedContentLength:[dataAtURL length] 
                                    textEncodingName:nil] ;
        result = [[NSCachedURLResponse alloc] initWithResponse:reponse data:dataAtURL
            userInfo:nil storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] ;

        #warning LEOPARD BUG MAKES IT SO I DONT AUTORELEASE result NSCachedURLResponse

        [reponse release] ;
        }
    else
        {
        NSLog( @"\tFulfilling from web" ) ;
        result = [super cachedResponseForRequest:request] ;
        }

    NSLog( @"Result = %@" , result ) ;

    return result ;
    }

在我的请求中,我指定了NSURLRequestReturnCacheDataDontLoad的缓存策略。这种方法似乎被称为好,但它在iPhone上有奇怪的行为。无论我是否返回NSCachedURLResponse实例,UIWebView仍然会返回错误:

Error Domain=NSURLErrorDomain Code=-1008 UserInfo=0x45722a0 "resource unavailable"

这就好像UIWebView忽略了这样一个事实,即它正在获得除了nil以外的东西并且无论如何都会失败。然后我怀疑并想知道整个URL加载系统是否只是忽略了来自-cachedResponseForRequest的所有内容,所以我创建了一个NSCachedURLResponse的子类,如下所示:

@implementation DebugCachedURLResponse

- (NSData *)data
    {
    NSLog( @"**** DebugCachedURLResponse data accessed." ) ;
    return [super data] ;
    }

- (NSURLResponse *)response
    {
    NSLog( @"**** DebugCachedURLResponse response accessed." ) ;
    return [super response] ;
    }

- (NSURLCacheStoragePolicy)storagePolicy
    {
    NSLog( @"**** DebugCachedURLResponse storagePolicy accessed." ) ;
    return [super storagePolicy] ;
    }

- (NSDictionary *)userInfo
    {
    NSLog( @"**** DebugCachedURLResponse userInfo accessed." ) ;
    return [super userInfo] ;
    }
@end

然后,我修改了-cachedResponseForRequest以使用此类:

    result = [[DebugCachedURLResponse alloc] initWithResponse:reponse data:dataAtURL
        userInfo:nil storagePolicy:NSURLCacheStorageAllowedInMemoryOnly] ;

我将请求设置为具有NSURLRequestUseProtocolCachePolicy缓存策略并运行该程序。虽然我可以看到我的-cachedResponseForRequest版本被调用并返回所有文件URL的DebugCachedURLResponses,但我没有调用任何调试,告诉我我的DebugCachedURLResponse实例被完全忽略!

所以,再来一次,我被困住了。我不认为你有任何其他想法?

非常感谢你的第一个回复。


1
2017-08-08 03:01



嗨leftspin - 我花了很多时间经历类似的考验和磨难。在之前的一些代码中,我认为它实际上接受了缓存的响应,但现在我得到了与你相同的结果(它得到了我的缓存响应但忽略了它)。既然你发布了这个运气好吗? - Tyler
不,抱歉,没有运气。 - leftspin
我刚刚在这里发布了一个类似问题的答案: stackoverflow.com/questions/1343515/... 我的方法是相同的:子类NSURLCache,但我从不调用任何[超级并完全依赖我自己的机制:我自己处理请求。即使我认为它不完全尊重缓存磁盘/内存的缓存方法,这对我来说也可以正常工作:所有内容都被强制转移到FS。但我想不出更优雅的解决方案:/ - yonel


嘿伙计们,我想我发现了问题。它看起来像iPhone上的NSURLRequestReturnCacheDataElseLoad和NSURLRequestReturnCacheDataDontLoad。当从缓存返回NSCachedURLResponse并且它包含指示内容已过期的HTTP标头(例如Expires,Cache-Control等)时,将忽略缓存的响应并请求原始源。

解决方案如下:

  1. 实现自己的NSHTTPURLResponse子类,允许您修改allHeaderFields字典。
  2. 实现您自己的NSURLCache,覆盖cachedResponseForRequest:并返回一个新的NSCachedURLResponse,其中包含NSHTTPURLResponse子类的实例,并剥离相关的“到期”HTTP标头。

1
2017-11-01 23:12



第2步不太清楚。你能发一些代码吗? - jm.