TN2277 Networking and Multitasking

多任务是iOS4的一个主要特色。多任务允许将你的应用搁置在后台。对于系统响应来说非常帮,但是可能严重影响你的应用和网络打交道的能力。这个技术说明揭示了如何最好地应对网络应用中地多任务。

介绍

多任务是在iOS4中引入的,为网络应用增加了复杂性。当iOS将你的应用放在后台,不久可能就会悬挂这个应用。当应用悬挂后,应用进程中的代码不再执行。这使得应用不可能继续处理进入的网络数据。更进一步的是,当应用悬挂后,系统可能选择收回应用使用的网络socket资源,因此关闭了socket代表的网络连接。
虽然iOS4已经小心翼翼地最小化网络应用和多任务兼容的工作量,为了维持应用的基本功能,还是需要做少量工作的。此外,在多任务存在的情况下,你可以极大地提升应用的网络行为。
这篇文档一开始解释使网络应用和多任务兼容的基本所需步骤,然后描述了一些实现细节,然后演示了如何使得网络应用在多任务环境中闪光。
在这之前,你应该熟悉iOS应用的生命周期,如iOS Application Programming Guide描述的一样。
具体的,这篇技术说明使用foreground,background,和suspend这些字眼。
最后,这篇技术说明对于那些应用中没有专门多任务性能的开发者而言。然而,如果你的应用适合多任务类别中的一个,正如iOS Application Programming Guide描述的,你应该确保阅读那部分信息。

基础

iOS所有的网络APIs最后是由BSD Sockets API实现的,为了这部分讨论的目的,支持两种类型的sockets:

  • listening sockets-侦听socket用来接受进来的TCP连接。
  • data sockets-数据socket用来在网络中传输数据。Data sockets通常代表TCP连接或者让你接入UDP端口。

下面的部分描述了如何应对每种socket类型的多任务。

Listening Socket

在多任务环境中处理侦听socket非常简单:当你的应用进入后台后,你应该关闭侦听socket,当应用又回到前台时,再次打开这个socket。这样的行为有两个重要原因:

  • 一旦 你的应用进入后台,它可能被悬挂。一旦它被悬挂,它没有能力在侦听socket中处理接进来的连接。然而,这个socket在kernel眼里仍然是激活态。如果有客户连接到这个socket,kernel会接受这个连接,但是你的应用不能交流。最后客户放弃连接,不过这会花费时间。因此,最好就是当应用进入后台的时候关闭侦听socket,kernel会立即拒绝接进来的连接。
  • 如果系统悬挂了你的应用,然后,从你的侦听socket收回资源,你的应用再也不能侦听连接,即使这个应用重新开始。应用可能注意这个,也可能忽略这个,取决于如何管理这个侦听socket。一般来说,当应用进入后台的时候关闭侦听socket来避免问题是最好的。

记住,当你的应用关闭侦听socket,它也应该停止socket上所有的Bonjour注册。

Data Socket

data socket的情形更加微妙,更多地取决于data socket在做什么。你要做的决定是当应用进入后台时是否关闭你的data socket,更重要的是,当应用悬挂的时候是否关闭你的data socket。考量的关键因素在于关闭和重启你的data socket简单不简单:

  • 如果重启你的data socket简单廉价,最好的方法就是当应用进入后台时关闭socket,当应用回到前台时重启socket。这使得你的生活很简单,事实上,如果你这样做,你可以忽略下面所要讨论的一切!
  • 如果重启你的data socket很慢,很昂贵,或者用户会察觉到,你最好使得socket处于开启状态。有个很好的例子,用户从你的应用切换到别的应用,然后马上又回到你的应用。在这种情形下,你不希望用户遭受网络延迟。然而,如果你让data socket开启,你必须处理这个决定的结果。

如果当应用进入后台的时候,你将你的data socket置于打开的状态,你必须处理socket的错误。处理错误时一个新的要求,但是在这种情形下尤其重要,如果你的应用悬挂了,kernel可能会收回socket的应用,这样socket上的所有网络操作都会失败。你对在这种情形下的socket能做的只有关闭它。

当你在data socket上使用的协议支持静态模式的话,你应该在应用进入后台时启用这个静态模式,当返回到前台时再禁止这个模式。例如,IMAP支持IDLE命令,当邮箱变化时,异步通知用户。当应用处于悬挂状态,让应用接受这些通知没有意义,所以应用进入后台后你绝对想要取消这些IDLE请求。

更高层的APIs

上面的部分涉及 了sockets,但是上面提及的建议也适用于高层。举例:

  • 如果你使用NSStream管理TCP,NSStream和CFStream是toll-free bridged。
  • 如果你使用NSURLConnection,它是由CFStream(CFHTTPStream)实现的,CFStream又使用另一个CFStream(CFSocketStream)管理下面的socket。

如果kernel收回了data socket,高层的construct会报道一个错误。你应该检测和处理这个错误。举例:

  • 如果你使用NSStream,你会收到NSStreamEventHasBytesAvailable事件。你应该读取这个流,[NSInputStream -read:maxLength:]会返回-1,指示有错。你应该呼叫[NSStream streamError]得到这个实际的错误。
  • 如果你使用NSURLConnection,这个connection会呼叫你的connection:didFailWithError:代理方法。

实现细节

这一部分描述了一些重要的事情,当你的应用响应多任务转换的时候应该切记这些事。

小心狗

正如前面描述的,当从前台切换到后台的时候,你的应用可能结束重要的任务,典型的是在-applicationDidEnterBackground:代理方法。如果你的应用在applicationDidEnterBackground中花费太长的时间,看门狗会杀掉你的应用。
在这种环境下,任何涉及等待网络的操作都是太久。因为当你的应用进入后台时,网络刚好变得没有响应。如果你的应用花费了太久的时间,它会成为狗的食物。
不是所有的网络操作涉及等待。例如,在applicationDidEnterBackground代理方法中关闭侦听socket很完美。关闭侦听socket总是非常快速。在这种情形下甚至可以传输数据。例如,如果你想要发送发送命令将连接变为安静模型,只要kernel的socket缓冲仍然有空间放置这个command。在那种情形下,发送这个命令指示将数据复制到socket buffer,不会阻塞很多时间。
应用在发送命令且等待响应的时候会陷入困境。如果网络没有响应,响应会延迟,你的应用会被看门狗杀掉。
如果你必须要等待网络,你应该使用后台任务([UIApplication beginBackgroundTaskWithExpirationHandler:])管理这个进程。例如,如果你的协议静态模式命令需要等待一个响应。在这种情形下,你的applicationDidEnterBackground:代理方法应该:

  1. 开始一个异步操作发送静态模式且等待响应
  2. 开始后台任务请求另外的时间,让操作完成
  3. 从applicationDidEnterBackground方法中返回

这会给你的应用额外的时间执行静态模式而不会花费太久在applicationDidEnterBackground方法中。
记住,所有的后台任务需要提供截止handler,当系统要求后台任务马上完成时会调用。这个截止handler,类似于applicationDidEnterBackground:代理方法,看门狗对其垂涎三尺。如果截止handler花费时间太长,应用被杀掉。因此在你确保截止handler中的网络操作不会等待网络才是安全的。这是你可能想要单方面关闭data socket的地方。

并发性

如果你计划在iOS应用中做一些有用的网络,你很大可能会使用后台任务继续你的工作,即使暂时的,当你的应用进入后台的时候。当你开始一个后台任务的时候,你必须提供一个期满handler,可以取消这个后台任务。期满handler在主线程中运行,当它返回的时候必须取消这个后台任务。正如提到的那样,期满handler必须快速执行,因为如果花的时间太多,你的应用会被看门狗杀掉。
这些要求对你的应用设置了一些限制。更严格的是,如果你在第二个线程使用同步的阻塞网络APIs,你很难满足这些要求。
举个例子,考虑当一个网络线程以同步阻塞的方式在侦听接进来的连接。在那种情况下,线程会被阻塞。当用户按下Home键,你的应用被放在了后台,你想要关闭侦听socket。你怎么样对accept中的线程截掉阻塞。没有好的办法。
另一方面,考虑当你的应用在后台做一个大文件下载,网络非常慢,应用用完了后台任务的时间。系统主线程运行后台任务的截止handler,应用在返回之前关闭这个连接。当线程阻塞等待网络数据的时候,你怎么办?没有好的办法。
这个结局就是,为了很好的支持多任务,你应该想要使用异步网络APIs。iOS提供了许多APIs来做这个-从低层的GCD到高层的APIs类似NSURLConnection,有许多存在于中间的-我们鼓励你使用它们。

Run Loops

如果你使用基于run loop的网络API,你会发现需要在期满handler中运行run loop让基于run loop的设施有时间结束。如果你这样做,你必须运行这个run loop在定制的run loop模式;在期满handler中运行默认run loop模式下的run loop我们不支持。记住,如果你使用这个技术,你必须调度有关的run loop source在你的定制run loop 模式中和默认模式中。

测试Socket Reclaim

如果你要编写代码应对被kernel收回socket 资源的情况,你必须要知道怎么测试。系统在怎么样的场景中会收回socket资源没有很好的文档。这给了我们在未来改进的可能。然而,在目前的系统(iOS 4.0到iOS 4.3),你可以让系统收回socket资源,这可以通过:

  1. 将你的应用放在后台
  2. 确保应用悬挂
  3. 锁住屏幕

当你的应用重现执行时,它会发现sockets已经被收回了。

基础之上

如果你应用需要执行长时间的网络传输,你有两个方式改进用户的经验:

  • 使用后台任务完成后台传输
  • 实现可重新开始的传输

后台任务

继续网络传输是后台任务的明显应用。如果用户开始大的传输然后跳出你的应用,它可以开始后台任务继续这个传输。如果所有都顺利,当用户下一次将应用带到前台的时候,这个传输已经完成了。
当在你的应用中实现后台任务支持时,你不需要有分开的,所谓后台前台的逻辑。每一次你的应用开始长时期的操作时开始后台任务,即使应用在前台。当应用在前台时,后台任务不会有任何效果。然而,如果用户将应用放到后台,后台任务的存在会自动的保持应用运行,以便于完成这个操作。

可重新开始的传输

即使你使用后台任务继续长传输,你的应用支持可重新传输仍然很重要。有很多场景需要:

  • 如果运行在一个不支持多任务的设备
  • 如果在传输过程中网络连接被打断
  • 如果你的应用要求开始后台任务但是系统拒绝
  • 如果系统资源少,系统必须在后台任务完成之前悬挂或者中止你的应用
  • 如果传输花费的时间多余后台任务允许

所有这些情形会从你实现可重新传输中获利。
实现可重新传输下载很简单。如果你使用HTTP,大多数服务端支持entity tags和byte ranges,可重新下载的基本。而且,在NSURLConnection使用这些特色很简单。你应该阅读HTTP协议学习更多。
可重新HTTP上传稍微有点棘手,也取决于你上传的服务端。如果不修改你的服务端,让服务端支持,不可能实现可重新上传。
实现可重现开始的FTP下载也很简单。当重新下载,你可以设置kCFStreamPropertyFTPFileTransferOffset特征指示CFFTPStream从哪里开始下载。
CFFTPStream不支持可重新开始的FTP上传。再一次,因为FTP可怜的安全特色(用户名,密码和数据都是明文传输的)。

多任务超级力量

一些应用当运行在多任务系统中具有特殊的力量。举个例子,一个音频播放应用可以在后台播放应用。这个部分解释网络和这些多任务超级力量的关系。
有两种类型的超级力量和网络有关:

  • 在后台执行代码
  • VoIP sockets

后台执行

最常见的多任务特殊力量是在后台执行代码的能力。举个例子,当一个音乐播放应用在播放音乐,当这个应用转移到后台时不会被悬挂。这个文档的大部分使用转移到后台这个字眼作为悬挂合格者的缩语。在这些应用中,转移到后台的动作不会自动使得应用变为悬挂合格者。当这些应用停止做那些使得它们不成为悬挂合格者的东西时,它们才成为悬挂合格者。继续音乐播放者这个例子,当这个应用停止播放音乐时,这个应用成为悬挂合格者。
注意后台任务是这种力量的特殊形式。任何应用通过开始后台任务可以在后台有限的时间内执行代码。这样的应用当它最后一个后台任务结束时成为悬挂合格者。

VoIP Sockets

一个VoIP应用应该一直运行,这样它可以监视VoIP控制连接;然而,为了减少对系统的存储影响,当它不活跃的时候,应用被悬挂。为了正常工作,这个应用需要将这个数据socket注册为VoIP socket。一个以这种形式注册的socket有两个特殊的特色:

  • 当这个应用悬挂的时候,系统替应用检测socket。如果有数据到达这个socket,系统重新执行这个应用。然后可以从socket读取数据,采取合适的动作
  • 这个socket的资源不会被收回。这样,这个app可以安全的被悬挂,不需要害怕它的socket变成聋子。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值