`
muyu114
  • 浏览: 136984 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

iOS-利用本地数据来代替远程UIWebView请求

 
阅读更多

在这篇文章中,我将讲述如何在iOS中的UIWebView中加载一个网页,使用修正的NSURLCache来用本地网页资源复本来代替基于远程网页的数据复本。

介绍

正常情况下当你需要写一个具备网络连接的iOS程序,你会想要一个本地的iOS接口能够接收网络上的所有数据。

然而,在项目中总是有一些限制你可以实现的东西,而且有时候你可能想要为用户显示一个规整的页面。

如果你打算采用这种方式,你最好确信网络接口尽可能流畅。你可以采取的措施之一是将图片的本地复本和其他非更新的资源包含到程序中。

为了在一个远程加载的网页中使用本地资源,或者需要远程页面以某种方式参考本地资源(例如通过URL主题),或者需要用本地地址来代替远程地址。

在这个文章中,我将讲述当网页包含远程资源时如何用本地资源来替代。

NSURLCache

在Mac上,你可以在WebViewDelegate上使用一系列不同的方式来实现,包括实现webView:resource:willSendRequest:redirectResponse:fromDataSource来使得NSURLRequest代替另一个。不幸的是,iOS中的UIWebViewDelegate并不如此好用因此我们需要以另外的方式来实现。

幸运的是,还有一点你可以利用:就是NSURLCache在几乎每个请求下都会被调用。

正常情况下,只有很少的数据存储在NSURLCache中,特别是在更旧的iOS设备上,这个存储区很小。即使你利用setMemoryCapacity:函数来增加这个缓存的大小,它相对于Mac上的NSURLCache来说还是太小了以至于不能存储资源。

当然在这个例子中那不是问题,因为我们将会子类化NSURLCache并且实现自定义的版本,该版本将保证可以存储我们所需的资源而且不需要pre-caching(在程序运行之前所有的资源都要保证准备在存储去内)。

cachedResponseForRequest:

唯一一个我们需要重写的函数是cachedResponseForRequest:,这能够允许我们在它发送前查看每一个请求而且如果我们需要的话返回本地数据。

在这个代码中,我会使用词典来将远程URL映射为在本地程序相关库中的资源的文件名。如果一个请求是指向特定的URL,那么将返回本地文件内容。

下面给出了这个词典。

12345678
-(NSDictionary*)substitutionPaths{return[NSDictionary dictionaryWithObjectsAndKeys:@"fakeGlobalNavBG.png",            @"http://images.apple.com/global/nav/images/globalnavbg.png",        nil];}

只要针对URL:http://image.apple.com/global/nav/images/globalnavbg.png请求发出,那么下面的cachedResponseForRequest:可以利用资源文件夹中的fakeGlobalNavBG.png文件来代替。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
-(NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request{// Get the path for the requestNSString*pathString =[[request URL] absoluteString];    // 判断我们是否为这个路径提供了替代资源NSString*substitutionFileName =[[self substitutionPaths] objectForKey:pathString];    if(!substitutionFileName){// 没有替代资源,返回默认值return[super cachedResponseForRequest:request];    }    // 如果我们已经创建了一个缓存实例,那么返回它NSCachedURLResponse*cachedResponse =[cachedResponses objectForKey:pathString];    if(cachedResponse){return cachedResponse;    }    // 获得替代文件的路径NSString*substitutionFilePath =[[NSBundle mainBundle]            pathForResource:[substitutionFileName stringByDeletingPathExtension]            ofType:[substitutionFileName pathExtension]];    NSAssert(substitutionFilePath,        @"File %@ in substitutionPaths didn't exist", substitutionFileName);    // 加载替代数据NSData*data =[NSData dataWithContentsOfFile:substitutionFilePath];    // 创建可缓存的响应NSURLResponse*response =[[[NSURLResponse alloc]            initWithURL:[request URL]            MIMEType:[self mimeTypeForPath:pathString]            expectedContentLength:[data length]            textEncodingName:nil]        autorelease];    cachedResponse =[[[NSCachedURLResponse alloc] initWithResponse:response data:data] autorelease];    // 为后续响应,把它加入我们的响应词典中if(!cachedResponses){        cachedResponses =[[NSMutableDictionary alloc] init];    }[cachedResponses setObject:cachedResponse forKey:pathString];    return cachedResponse;}

设置我们的缓存区作为共享缓存

一个UIWebView试图使用当前的+[NSURLCache sharedURLCache]。为了调用我的代码,你需要创建一个NSURLCache的子类并且调用+[NSURLCache setSharedURLCache:]。

这里需要注意:一旦你设置新的网络缓存,你可能打算保持它工作直到你的程序退出。

当UIWebView向你的NSURLCache请求资源时,它假设NSURLCache具备NSCachedURLResponse。如果当UIWebView正在使用它的时候你释放了NSCachedURLResponse,有可能你的程序会崩溃。

不幸的是,迫使WebKit释放它的参考(references)—在某些例子里它何时释放是不确定的。只有WebKit去调用removeCachedResponseForRequest:的时候它才通知你可以丢弃那些资源。

这意味着你必须保证程序中只有一个NSURLCache,在application:didFinishLaunchingWithOptions方法中进行设置并且不要移去它。

一个限制

显然地,如果你设置了要用来存储本地数据的缓存区,只有一个查看缓存区的请求才是使其生效。

这意味这如果URL请求是requestWithURL:cachePolicy:timeoutInterval:,缓存策略是NSURLRequestReloadIgnoringCacheData,那么这个请求将忽略本地替代。

默认情况下,NSURLRequests的缓存策略是NSURLRequestUseProtocolCachePolicy。这个HTTP的缓存策略是相当复杂的而且我从来没有见过一个正常的NSURLRequest忽视缓存,这些规则可能会在某些情况下产生它忽视缓存的情况。如果这些情况发生的话,你的程序应该保持正常工作。

本地替代缓存示例程序
LocalSubstitutionCache.zip
下面是程序截图

利用我们的NSURLCache子类调用了后,顶部灰色链接栏上的灰色链接按钮被在本地资源文件中的蓝色图像所代替。

结论

这个工作的意图是允许UIWebView响应更灵敏而且更像本地用户界面。

事实上,UIWebView决不会具有本地用户界面那样的集成度和灵敏的响应。但是

使得本地存储尽可能多的资源有助于尽可能少的带给用户不好的体验。

原文作者:Matt Gallagher

原文链接:http://cocoawithlove.com/2010/09/substituting-local-data-for-remote.html

Substituting local data for remote UIWebView requests

In this post, I’ll show you how you can load a webpage in a UIWebView in iOS while using a modified NSURLCache to substitute local copies of resources within the webpage for the remote copies referred to by the actual page.

Introduction

Normally if you’re writing an iOS app with network connectivity, you’ll want to put a native iOS interface on all data received over the network.

However, there are always scheduling and other constraints on a project that limit what you can implement and sometimes you may simply choose to show a regular, webpage to the user.

If you choose to take this approach, it is best to make sure the web interface feels as smooth as possible. One of the steps you can take to ensure this is to include local copies of all image and other non-updating resources within the application itself.

To use a local resource in an iOS webpage loaded from a remote location, either the remote page must refer to the local resource in some way (e.g. through a custom URL scheme) or you must swap a local location in place of a remote locations.

In this post, I’ll look at how we can substitute a local resource when the webpage contains references to remote resources.

NSURLCache

On the Mac, you could use a range of different approaches in the WebViewDelegate to do this, including implementing webView:resource:willSendRequest:redirectResponse:fromDataSource: to substitute one NSURLRequest for another. Unfortunately, the UIWebViewDelegate in iOS is not nearly as capable so we need to do this another way.

Fortunately, there is one point you can hook into that is invoked for (almost) every request: the NSURLCache.

Normally, very little is actually cached in the NSURLCache, particularly on older iOS devices where the cache size is downright miniscule. Even if you use the setMemoryCapacity: method to increase the size of the cache, it seems significantly less likely to store resources than the NSURLCache on the Mac.

Of course that doesn’t matter in this case, since we’re going to subclass NSURLCache and implement our own version that will be guaranteed to hold all the resources we need and won’t need pre-caching (all the resources will be there before the program is started).

cachedResponseForRequest:

The only important method we need to override is cachedResponseForRequest:. This will allow us to examine every request before it is sent and return local data if we prefer.

For this code, I’ll use a dictionary that maps remote URLs to local file names in the Resources folder of the application bundle. If any request is made for the specified URLs, the contents of the local file will be returned instead.

So given the following dictionary containing a single path for substitution

12345678
-(NSDictionary*)substitutionPaths{return[NSDictionary dictionaryWithObjectsAndKeys:@"fakeGlobalNavBG.png",            @"http://images.apple.com/global/nav/images/globalnavbg.png",        nil];}

The following cachedResponseForRequest: implementation will substitute the contents of the fakeGlobalNavBG.png file in the Resources folder any time the URL http://images.apple.com/global/nav/images/globalnavbg.png is requested

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
-(NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request{// Get the path for the requestNSString*pathString =[[request URL] absoluteString];    // See if we have a substitution file for this pathNSString*substitutionFileName =[[self substitutionPaths] objectForKey:pathString];    if(!substitutionFileName){// No substitution file, return the default cache responsereturn[super cachedResponseForRequest:request];    }    // If we've already created a cache entry for this path, then return it.NSCachedURLResponse*cachedResponse =[cachedResponses objectForKey:pathString];    if(cachedResponse){return cachedResponse;    }    // Get the path to the substitution fileNSString*substitutionFilePath =[[NSBundle mainBundle]            pathForResource:[substitutionFileName stringByDeletingPathExtension]            ofType:[substitutionFileName pathExtension]];    NSAssert(substitutionFilePath,        @"File %@ in substitutionPaths didn't exist", substitutionFileName);    // Load the dataNSData*data =[NSData dataWithContentsOfFile:substitutionFilePath];    // Create the cacheable responseNSURLResponse*response =[[[NSURLResponse alloc]            initWithURL:[request URL]            MIMEType:[self mimeTypeForPath:pathString]            expectedContentLength:[data length]            textEncodingName:nil]        autorelease];    cachedResponse =[[[NSCachedURLResponse alloc] initWithResponse:response data:data] autorelease];    // Add it to our cache dictionary for subsequent responsesif(!cachedResponses){        cachedResponses =[[NSMutableDictionary alloc] init];    }[cachedResponses setObject:cachedResponse forKey:pathString];    return cachedResponse;}

Setting our cache as the shared cache

AUIWebView will try to use the current +[NSURLCache sharedURLCache]. To get our code called, you’ll need to create an instance of our NSURLCache subclass and invoke +[NSURLCache setSharedURLCache:].

A big warning here: once you set a new web cache, you probably want to leave it set until your program exits.

When the UIWebView requests resources from your NSURLCache, it assumes that the NSURLCache retains the NSCachedURLResponse. If you release the NSCachedURLResponse while any UIWebView is using it, it will probably crash your app.

Unfortunately, it is pretty hard to force WebKit to let go of its references — it can hold onto them indefinitely in some cases. Until WebKit itself chooses to invoke removeCachedResponseForRequest: to tell you that you can throw away the resource you must hold onto it.

What this means is that you should only have one NSURLCache in your program. Set it in your application:didFinishLaunchingWithOptions: method and never remove it.

A limitation…

Obviously, if you’re overriding the cache to substitute local data, it will only work if the request actually looks at the cache.

This means that if the URL is requested with requestWithURL:cachePolicy:timeoutInterval: with a cache policy of NSURLRequestReloadIgnoringCacheData, the the request will bypass this local substitution.

By default, NSURLRequests have a cache policy of NSURLRequestUseProtocolCachePolicy. The HTTP cache policy is pretty complicated and while I’ve never actually seen a normal NSURLRequest actually bypass the cache, the number of rules involved create a situation where it seems like it may be possible in some situations. Your app should not misbehave if this were to happen for some reason.

The LocalSubstitutionCache sample app

You can download theLocalSubstitutionCache.zip(66kb) sample project

Here’s a small screenshot of today’shttp://www.apple.comrunning in a UIWebView

After invoking +[NSURLCache setSharedURLCache:] with our NSURLCache subclass, the gray links bar across the top are replaced with a blue graphic stored in the app’s bundle

Conclusion

The purpose of this work is to allow UIWebViews to feel more responsive and a bit more like native user-interfaces.

In reality, a UIWebView will never feel as responsive or integrated as a native user-interface but sometimes making one screen of your app a remote webpage is a big enough saving in developer resources that you’re prepared to make the sacrifice in user quality. Making sure as many resources as possible are stored locally will help make any negative impact on user quality as minor as possible.


分享到:
评论

相关推荐

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

    为了解决这个问题,开发者可以利用本地数据来代替远程UIWebView请求,这样即使在网络不可用时也能提供内容。这个"iphone 利用本地数据来代替远程UIWebView请求实例"就是针对这一需求的解决方案。 首先,我们要理解...

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

    在iOS开发中,为了优化用户体验和减少网络流量,开发者经常需要缓存网络请求的数据,特别是对于UIWebView的加载内容。`NSURLCache`是苹果提供的一个关键类,它允许我们在本地存储HTTP和HTTPS请求的响应,以便在后续...

    ios ui webview

    - 自iOS 8起,苹果推荐使用WKWebView代替UIWebView,因为它提供了更好的性能和现代Web标准的支持。 - WKWebView通过WKWebViewConfiguration对象可配置更多的安全和隐私选项。 总的来说,“ios ui webview”示例将...

    [附源码+数据库+毕业论文+部署教程+配套软件]基于SpringBoot+MyBatis+MySQL+Maven+Vue的停车场管理系统,推荐!

    一、项目简介 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷 二、技术实现 jdk版本:1.8 及以上 ide工具:IDEA或者eclipse 数据库: mysql5.5及以上 后端:spring+springboot+mybatis+maven+mysql 前端: vue , css,js , elementui 三、系统功能 1、系统角色主要包括:管理员、用户 2、系统功能 前台功能包括: 用户登录 车位展示 系统推荐车位 立即预约 公告展示 个人中心 车位预定 违规 余额充值 后台功能: 首页,个人中心,修改密码,个人信息 用户管理 管理员管理 车辆管理 车位管理 车位预定管理,统计报表 公告管理 违规管理 公告类型管理 车位类型管理 车辆类型管理 违规类型管理 轮播图管理 详见 https://flypeppa.blog.csdn.net/article/details/146122666

    springboot656基于java-springboot的农机电招平台毕业设计(代码+数据库+论文+PPT+演示录像+运行教学+软件下载).zip

    项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea 数据库:MySql 部署环境:maven 数据库工具:navica 更多毕业设计https://cv2022.blog.csdn.net/article/details/124463185

    Python程序设计学习思维导图-仅供参考

    内容为Python程序设计的思维导图,适用于新手小白进行浏览,理清思路

    2024-Stable Diffusion全套资料(软件+关键词+模型).rar

    2024-Stable Diffusion全套资料(软件+关键词+模型).rar

    mmexport1741417035005.png

    mmexport1741417035005.png

    COMSOL三维锂离子电池全耦合电化学热应力模型:模拟充放电过程中的多物理场耦合效应及电芯内应力应变情况,COMSOL锂离子电池热应力全耦合模型,comsol三维锂离子电池电化学热应力全耦合模型锂离子

    COMSOL三维锂离子电池全耦合电化学热应力模型:模拟充放电过程中的多物理场耦合效应及电芯内应力应变情况,COMSOL锂离子电池热应力全耦合模型,comsol三维锂离子电池电化学热应力全耦合模型锂离子电池耦合COMSOL固体力学模块和固体传热模块,模型仿真模拟电池在充放电过程中由于锂插层,热膨胀以及外部约束所导致的电极的应力应变情况结果有电芯中集流体,电极,隔膜的应力应变以及压力情况等,电化学-力单向耦合和双向耦合 ,关键词: 1. COMSOL三维锂离子电池模型; 2. 电化学热应力全耦合模型; 3. 锂离子电池; 4. 固体力学模块; 5. 固体传热模块; 6. 应力应变情况; 7. 电芯中集流体; 8. 电极; 9. 隔膜; 10. 电化学-力单向/双向耦合。,COMSOL锂离子电池全耦合热应力仿真模型

    基于传递矩阵法的一维层状声子晶体振动传输特性及其优化设计与应用,声子晶体传递矩阵法解析及应用,Matlab 一维层状声子晶体振动传输特性 传递矩阵法在声子晶体的设计和应用中具有重要作用 通过调整声子

    基于传递矩阵法的一维层状声子晶体振动传输特性及其优化设计与应用,声子晶体传递矩阵法解析及应用,Matlab 一维层状声子晶体振动传输特性 传递矩阵法在声子晶体的设计和应用中具有重要作用。 通过调整声子晶体的材料、周期和晶格常数等参数,可以设计出具有特定带隙结构的声子晶体,用于滤波、减震、降噪等应用。 例如,通过调整声子晶体的周期数和晶格常数,可以改变带隙的位置和宽度,从而实现特定的频率范围内的噪声控制。 此外,传递矩阵法还可以用于分析和优化声子晶体的透射谱,为声学器件的设计提供理论依据。 ,Matlab; 一维层状声子晶体; 振动传输特性; 传递矩阵法; 材料调整; 周期和晶格常数; 带隙结构; 滤波; 减震; 降噪; 透射谱分析; 声学器件设计,Matlab模拟声子晶体振动传输特性及优化设计研究

    头部姿态估计(HeadPose Estimation)-Android源码

    头部姿态估计(HeadPose Estimation)-Android源码

    永磁同步电机FOC、MPC与高频注入Simulink模型及基于MBD的代码生成工具,适用于Ti f28335与dspace/ccs平台开发,含电机控制开发文档,永磁同步电机控制技术:FOC、MPC与高

    永磁同步电机FOC、MPC与高频注入Simulink模型及基于MBD的代码生成工具,适用于Ti f28335与dspace/ccs平台开发,含电机控制开发文档,永磁同步电机控制技术:FOC、MPC与高频注入Simulink模型开发及应用指南,提供永磁同步电机FOC,MPC,高频注入simulink模型。 提供基于模型开发(MBD)代码生成模型,可结合Ti f28335进行电机模型快速开发,可适用dspace平台或者ccs平台。 提供电机控制开发编码器,转子位置定向,pid调试相关文档。 ,永磁同步电机; FOC控制; MPC控制; 高频注入; Simulink模型; 模型开发(MBD); Ti f28335; 电机模型开发; dspace平台; ccs平台; 编码器; 转子位置定向; pid调试。,永磁同步电机MPC-FOC控制与代码生成模型

    light of warehouse.zip

    light of warehouse.zip

    考虑温度和气体排放等因素的工业乙醇发酵过程及其Matlab源码-乙醇发酵-气体排放-Matlab建模和仿真-代谢路径

    内容概要:文章深入讨论了工业乙醇发酵的基本原理及工艺流程,特别是在温度和气体排放(如CO2及其他有害气体)影响下的发酵效果分析。文章介绍了乙醇发酵的重要环节,如糖分解、代谢路径、代谢调控以及各阶段的操作流程,重点展示了如何通过Matlab建模和仿真实验来探索这两个关键环境因素对发酵过程的具体影响。通过动态模型仿真分析,得出合适的温度范围以及适时排除CO2能显著提升发酵产乙醇的效果与效率,从而提出了基于仿真的优化发酵生产工艺的新方法。 适用人群:从事生物工程相关领域研究的科学家、工程师及相关专业师生。 使用场景及目标:适用于实验室环境、学术交流会议及实际生产指导中,以提升研究人员对该领域内复杂现象的理解能力和技术水平为目标。 其他说明:附录中有详细的数学公式表达和程序代码可供下载执行,便于有兴趣的研究团队重复实验或者继续扩展研究工作。

    Tomcat资源包《Tomcat启动报错:CATALINA-HOME环境变量未正确配置的完整解决方案》

    本资源包专为解决 Tomcat 启动时提示「CATALINA_HOME 环境变量未正确配置」问题而整理,包含以下内容: 1. **Apache Tomcat 9.0.69 官方安装包**:已验证兼容性,解压即用。 2. **环境变量配置指南**: - Windows 系统下 `CATALINA_HOME` 和 `JAVA_HOME` 的详细配置步骤。 - 常见错误排查方法(如路径含空格、未生效问题)。 3. **辅助工具脚本**:一键检测环境变量是否生效的批处理文件。 4. **解决方案文档**:图文并茂的 PDF 文档,涵盖从报错分析到成功启动的全流程。 适用场景: - Tomcat 9.x 版本环境配置 - Java Web 开发环境搭建 - 运维部署调试 注意事项: - 资源包路径需为纯英文,避免特殊字符。 - 建议使用 JDK 8 或更高版本。

    java毕业设计源码 仿360buy京东商城源码 京东JavaWeb项目源代码

    这是一款仿照京东商城的Java Web项目源码,完美复现了360buy的用户界面和购物流程,非常适合Java初学者和开发者进行学习与实践。通过这份源码,你将深入了解电商平台的架构设计和实现方法。欢迎大家下载体验,提升自己的编程能力!

    java-springboot+vue的乒乓球馆预约管理系统源码.zip

    系统选用B/S模式,后端应用springboot框架,前端应用vue框架, MySQL为后台数据库。 本系统基于java设计的各项功能,数据库服务器端采用了Mysql作为后台数据库,使Web与数据库紧密联系起来。 在设计过程中,充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。

    【javaweb毕业设计源码】大学生求职就业网

    这是一款专为大学生打造的求职就业网JavaWeb毕业设计源码,功能齐全,界面友好。它提供简历投递、职位搜索、在线交流等多种实用功能,能够帮助你顺利进入职场。无论你是想提升技术水平还是寻找灵感,这个源码都是不可多得的资源。快来下载,让你的求职之路更加顺畅吧!

    useTable(1).ts

    useTable(1).ts

    DSP实验报告汇总.pdf

    实验一: 1、进行CCS6.1软件的安装,仿真器的设置,程序的编译和调试; 2、熟悉CCS软件中的C语言编程; 3、使用按键控制LED跑马灯的开始与停止、闪烁频率; 4、调试Convolution、FFT、FIR、FFT-FIR实验,编制IIR算法并调试,并在CCS软件上给出实验结果。 实验二: 1、利用定时器周期中断或下溢中断和比较器比较值的修改来实现占空比可调的PWM波形; 2、改变PWM占空比控制LED灯的亮暗,按键实现10级LED灯亮暗调整; 3、模拟数字转换,转换过程中LED指示,并在变量窗口显示转换结果; 4、数字模拟转换,产生一个正弦波,转换过程中LED指示,转换完成后在CCS调试窗口显示波形。 实验三: 1、SCI异步串行通信实验; 2、SPI及IIC同步串行通信实验; 3、CAN现场总线串行通信实验; 4、传输过程中LED指示。 实验四: 1、电机转速控制实验。

Global site tag (gtag.js) - Google Analytics