`

关于如何在UIWebView中实现缓存(转)

 
阅读更多

可能很多人使用过UIWebView,也考虑过如何在离线状态下读取缓存的问题,但是好像没有一个标准的答案。有的人说可以,有的人说不行。通过这几天的研究,我发现了一些问题,在这里希望各位给点意见。

另外,还要求助一下,有没有人知道在ios上测试网页加载速度的工具啊? 找了好久都没看到。最好是免费的。
一.HTML5 , Manifest
最开始我的想法是使用HTML5中的离线存储功能,也就是分析Manifest文件来存储和更新部分资源文件。但是经过实践发现,UIWebView根本不支持HTML5,他只实现了Webkit中页面渲染的那一部分。所以要实现缓存必须要另辟蹊径。

二.ASIHTTPRequest,ASIDownloadCache  和 ASIWebPageRequest
   首先我得说,这确实是个很好的框架,使用起来确实很方便,但是对于缓存这个问题,好像也跟第二点提到的效果差不多,加载速度没有明显的提升,离线模式下也无法加载。这是实现的代码


-(void)loadURL:(NSURL*)url
{
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    //ASIWebPageRequest *request= [ASIWebPageRequest requestWithURL:url];
    [request setDelegate:self];
    //[request setUrlReplacementMode:ASIReplaceExternalResourcesWithData];
    [request setDidFailSelector:@selector(webPageFetchFailed:)];
    [request setDidFinishSelector:@selector(webPageFetchSucceeded:)];
     //设置缓存
    [request setDownloadCache:[ASIDownloadCache sharedCache]];
    //[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
    [request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
    [request setDownloadDestinationPath:[[ASIDownloadCache sharedCache]pathToStoreCachedResponseDataForRequest:request]];
     [request startAsynchronous];
}


- (void)webPageFetchFailed:(ASIHTTPRequest *)theRequest
{
    // Obviously you should handle the error properly...
    NSLog(@"%@",[theRequest error]);
    NSString *path = [[NSBundle mainBundle] pathForResource:@"error1.html" ofType:nil inDirectory:@"WebResources/Error"];
    NSURL  *url=[NSURL fileURLWithPath:path];  
    [viewer loadRequest:[NSURLRequest requestWithURL:url]];
}

- (void)webPageFetchSucceeded:(ASIHTTPRequest *)theRequest
{
    NSString *response = [NSString stringWithContentsOfFile:
                          [theRequest downloadDestinationPath] encoding:[theRequest responseEncoding] error:nil];
    // Note we're setting the baseURL to the url of the page we downloaded. This is important!
    [viewer loadHTMLString:response baseURL:[theRequest url]];
    //[viewer loadHTMLString:response baseURL:nil];
}

 

.NSURLCache

  尽管在官方的说明文档里面说到NSURLCache和NSCachedURLResponse可以用于缓存,但经我测试好像仅仅只能用于加载本地某些资源文件(这里有一篇博客,原文是英文的,这是翻译过来的
,而且还有大小的限制(好像根据iphone的版本不同而不同,最小是25KB吧),比如图片和JS代码, 而对于整体的页面无法进行加载。而且经过测试也没有感觉加载速度有明显的提高,我用的缓存策略是NSURLRequestReturnCacheDataElseLoad(可能是没有读取本地的缓存文件?),离线模式下也无法加载(可能是baseURL的关系?)。
这找到一篇博客,一种新的解决思路,经过我测试,可以很好的实现缓存。
另外做一点引申,对于动态获取数据的页面,我们不需要缓存的那些请求,只要过滤掉就可以了。
先新建一个文件,把所有不需要缓存的请求的URL写在一个文件里,就象HTML5的 Cache Manifest那样。
然后需要使用缓存的时候读取这个文件,并在重写的- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request  这个方法内对请求进行判断,如果是属于这个文件内的,比如web service的请求就直接返回,其他的就继续处理。

 
四.利用NSURLProtocol实现离线缓存(推荐 对于一般简单缓存)
 

搜索解决方案的时候找到了Rob Napier 的博文:Drop-in offline caching for UIWebView (and NSURLProtocol) 文章介绍了使用NSURLProtocol实现UIWebView的离线缓存的简单实现,你可以在github上下载这个demo的代码。

rob认为无论是“MKNetworkKit”还是”AFCache”实现的缓存都过于复杂,而他想要的是一个简单机制:

1、你使用了UIWebView指向来显示一个有图像嵌入的网站。
2、当你的设备online时,你有正常的缓存算法。
3、当你的设备offline时,你可以显示页面的最后一个版本。

这个demo里做了一个很简单的测试:将cnn.com运行一次,然后再将网络断掉,去浏览这些数据。

现有解决方案:

Matt Gallagher 有一些有趣的想法,使用NSURLCache的子类来实现,但是Rob发现这是不可靠的,尤其是iOS5的HTTP缓存规则十分复杂,在许多情况下如果你不访问服务器便不能获知你缓存的数据是否有效。另外,一些必要的素材如果没有被缓存,那么在离线时前期做的缓存工作就实效了。(辉:NSURLCache实现离线阅读的一点小心得 我也曾讨论了一些相关问题)

AFCache也被认为是一个很好的解决方案(辉:有时间我会对这个开源库进行详细评估,表面上看就是connection、NSURLCache、NSURLProtocol的综合解决方案)。短时间内作者并没有使测试通过,但是AFCache的作者也在文章后边回复说,采纳了Rob的想法,已经提交代码到github上。

要点:
1、尽早注册你的URLProtocol(application:didFinishLaunchingWithOptions:)。
2、NSURLProtocol是NSURLConnection的handler。NSURLConnection的每个请求都会去便利所有的Protocols,并询问你能处理这个请求么(canInitWithRequest: )。如果这个Protocol返回YES,则第一个返回YES的Protocol会来处理这个connection。Protocols的遍历是反向的,也就是最后注册的Protocol会被优先判断。
3、 当你的handler被选中了,connection就会调用–> initWithRequest:cachedResponse:client:,紧接着会调用–>startLoading。然后你需要负责回调:–>URLProtocol:didReceiveResponse:cacheStoragePolicy:,有些则会调用:–>URLProtocol:didLoadData:, 并且最终会调用–>URLProtocolDidFinishLoading:。你有没有发现这些方法和NSURLConnection delegate的方法非常类似——这绝非偶然!
4、当online的情况下,RNCachingURLProtocol只是负责将请求转发给一个新的NSURLConnection,并且拷贝一份结果给原来的connection。offline时, RNCachingURLProtocol就会从磁盘里载入先前的结果,并将这些数据发回给连接。整个过程只有区区200行代码(不包含Reachability)。
5、这里还有一个有趣的问题,就是当RNCachingURLProtocol创建了一个新的NSURLConnection的,即新的connection也会去找一个handler。 如果RNCachingURLProtocol说可以处理,那么就死循环了。怎么解决呢?通过添加自定义HTTP Header(X-RNCache)来标记这个请求,告诉RNCachingURLProtocol不要再处理这个请求。
6、它可以响应所有的connection,所以你可能需要修改canInitWithRequest: 选择你要缓存的数据。

另外:并发请求或复杂网络请求的缓存请使用MKNetworkKit(我们也在一个项目中使用了这个类库,非常轻量快捷是ASI的很不错的替代品)。

总结一下:
这项技术不是用来替代AFCache、MKNetworkKit的,他只是用来解决独立的、简单问题的(当然它也可以通过复杂实现来解决复杂问题)。 NSURLProtocol是非常强大的,Rob已经使用它来监听网络流量(如PandoraBoy中的几个ProxyURLProtocol类)。它非常值得你将其添加到你的工具箱中。

实例代码下载:https://github.com/rnapier/RNCachingURLProtocol

分享到:
评论

相关推荐

    UIWebView缓存网页

    通过这样的方式,我们可以实现UIWebView的离线缓存功能,使用户在没有网络的情况下也能访问之前加载过的网页。这在航班、地下铁等网络不稳定或不可用的场景下尤其有用,提高了应用的用户体验。同时,需要注意的是,...

    UIWebView离线缓存

    在iOS中实现UIWebView的离线缓存,开发者需要考虑以下几个关键点: 1. **配置缓存策略**:在发起网络请求时,可以通过设置NSURLRequest对象的缓存策略,如`NSURLRequestUseProtocolCachePolicy`或`...

    iOS-UIWebview缓存并保证实时性

    4. 实现缓存:对于每个拦截的请求,你需要检查是否有本地缓存。如果有,直接从缓存中读取内容并返回,否则向服务器发起请求。你可以使用`UserDefaults`、`SQLite`或者`CoreData`来存储缓存数据,但更推荐使用`...

    iOS UIWebView的缓存

    当没有网络连接时,我们需要手动检查UIWebView是否可以从缓存中加载页面。可以监听网络状态的变化,并在断网时加载本地存储的HTML文件,这些文件在有网时已经通过UIWebView加载并缓存过。 5. **图片缓存**: 对于...

    [转] 为UIWebView实现离线浏览

    当用户首次访问网页时,浏览器会自动下载这些资源并缓存到本地,即使在网络断开后,也能从缓存中加载。 2. **UIWebView与AppCache**: 在iOS中,UIWebView支持HTML5特性,包括AppCache。因此,我们可以通过在HTML...

    iOS UIWebView URL拦截.pdf

    如果需要,我们就创建一个没有实际内容的NSCachedURLResponse对象,并将其存储在缓存中。这样做是为了防止系统在后续操作中释放这个响应对象导致应用崩溃。同时,确保所有响应对象都真实存在于缓存中,符合iOS系统的...

    UIWebView Demo代码

    在iOS开发中,UIWebView是苹果...以上就是关于UIWebView的主要知识点,通过这个Demo,开发者可以学习如何在iOS应用中集成网页内容,并实现基本的交互功能。不过,随着技术的发展,建议新项目优先考虑使用WKWebView。

    webViewDemo:网易新闻中新闻详情页UIWebView的实现

    在实际应用中,UIWebView的默认缓存可能不足以满足特定需求,比如对缓存大小的控制、缓存更新策略或者对离线访问的支持。因此,开发者可能编写了自己的缓存系统,通过HTTP请求管理器(如NSURLConnection或...

    Mac XCode iPhone 显示一个UIImageView 和一个UIWebView

    在“Hello2”这个项目中,我们可以预期它包含了一个简单的示例,演示了如何在同一个界面中同时展示UIImageView和UIWebView。这可能是为了教学目的,让初学者了解如何布局多个UI元素并处理它们的交互。学习这样的基础...

    UIWebview使用缓存并且保证实时性

    作者dengjunwen,源码...NSURLRequestUseProtocolCachePolicy 缓存策略定义在 web 协议实现中,用于请求特定的URL。是默认的URL缓存策略。 NSURLRequestReloadIgnoringLocalCacheData 从服务端获取数据,忽略本地缓存 ...

    NSURLCache让本地数据来代替远程UIWebView请求

    3. 如果没有缓存或缓存已失效,请求会正常发送到服务器,并在接收到响应后将其存储到缓存中,供后续使用。 使用`NSURLCache`与`UIWebView`配合的步骤: 1. 配置`NSURLCache`:首先,我们需要创建一个`NSURLCache`...

    iOSUIWebView 优化 内存大大降低

    在iOS开发中,UIWebView是用于展示网页内容的重要组件,但它常常被诟病为内存消耗大,容易引发应用性能问题。本篇文章将深入探讨如何针对UIWebView进行优化,以实现内存的有效管理和降低。 首先,我们需要理解...

    ios WebView 页面缓存

    在iOS开发中,WebView是用于展示网页内容的重要组件,它允许我们在原生应用中嵌入HTML、CSS和JavaScript代码,实现丰富的交互体验。本篇主要探讨WebView页面缓存的实现和优化,这对于提升用户体验和减少网络请求至关...

    ios-UIWebView简易浏览器

    这个项目"ios-UIWebView简易浏览器"很显然是一个基于UIWebView实现的简单Web浏览器应用。下面我们将深入探讨UIWebView的基本用法、功能以及如何构建这样的一个简易浏览器。 1. **UIWebView的基本介绍** UIWebView...

    UIWebView高级应用

    在iOS开发中,UIWebView是苹果提供的一种非常重要的组件,用于在应用程序内嵌入网页内容。这个组件使得开发者能够展示HTML、CSS和JavaScript等Web技术创建的页面,从而实现丰富的交互性和动态性。本篇文章将深入探讨...

    ios-一个视图中两个控制器来回的切换 UISegmentedControl UIwebView.zip

    在本示例中,"ios-一个视图中两个控制器来回的切换 UISegmentedControl UIwebView.zip" 提供了一个使用UISegmentedControl来切换UIWebView展示内容的实现方式。这个场景广泛应用于构建具有导航菜单的应用,如“菜单...

    网页缓存到本地.zip

    "网页缓存到本地.zip"这个压缩包文件很可能包含了一个示例项目或教程,教授如何在iOS应用中实现WKWebView的网页数据缓存功能。 在iOS中,Web视图提供了多种缓存机制: 1. **HTTP 缓存**:当网页请求通过HTTP或...

    iphone 利用本地数据来代替远程UIWebView请求实例

    在iOS开发中,UIWebView是用于展示网页内容的组件,它允许我们在应用程序中嵌入HTML、CSS和JavaScript代码。然而,频繁的网络请求可能导致用户体验下降,特别是在离线或网络不稳定的情况下。为了解决这个问题,...

    IOSwebview缓存

    为了实现缓存功能,我们需要配置WKWebView的配置对象`WKWebViewConfiguration`。可以通过以下步骤设置: 1. 创建`WKWebViewConfiguration`实例。 2. 设置`WKWebsiteDataStore`,这用于管理Web内容的数据,包括缓存...

    HTML5App:在 UIWebView 中显示 JQuery Mobile 的示例源代码

    UIWebView是iOS SDK中的一个组件,允许开发者在原生iOS应用中嵌入网页内容。JQuery Mobile则是一款强大的前端框架,专注于为移动设备提供触摸优化的用户界面。 在这个示例中,我们将探讨如何使用HTML5、CSS3和...

Global site tag (gtag.js) - Google Analytics