当用户不太想用你的应用的时候,系统会让它进入后台状态。对于很多应用来说,后台状态只是进入挂起(suspended)状态过程中一个短暂的瞬间。挂起应用是提高电池寿命的一种方式,它还允许系统将重要的系统资源分配给新的引起用户注意的前台应用。
大多数应用进入到挂起状态很容易,但还是有一些让应用继续在后台运行的合理理由。一个徒步应用或许希望能全程跟踪用户的位置,这样它就能显示覆盖在徒步地图上的行程。一个音乐应用或许希望能在锁屏的时候继续播放音乐。其他一些应用或许希望在后台下载内容,这样它就能以最小的延时来向用户呈现内容。当你发现有必要让你的应用运行在后台的时候,iOS会高效的帮你做到,并且不会浪费系统资源或者用户的电量。iOS提供的技术分为三类:
- 在前台启动一个简短任务的应用,能在应用进入后台的时候请求一点时间来完成这个任务。
- 在前台启动下载任务的应用,能把这些下载任务的管理权交给系统,这样,就能在下载期间允许应用被挂起或者终止。
- 需要运行在后台来支持特定类型的任务的应用,可以声明对一个或多个后台执行模式的支持。
要尽量避免在后台工作,除非所作的事情有利于改善用户的综合体验。应用进入后台的原因可能是用户启动了另一个应用、或者因为用户锁定了设备并且现在它没有在运行。在这两种情况下,用户都表明现在你的应用不需要做任何有意义的事。在这种前提下,继续运行只会浪费设备的电量,并有可能导致用户强制退出一的应用。所以要注意你在后台所作的事,尽可能避免这种情况。
执行有限长度的任务
进入后台的应用被期望能尽快的进入休眠状态,一遍它们能被系统挂起。如果你的应用正在任务中,并且需要一点额外的事件来完成任务,它可以调用UIApplication对象的beginBackgroundTaskWithName:expirationHandler: 或beginBackgroundTaskWithExpirationHandler:方法来请求一些额外的执行时间。调用这些方法可以临时推迟应用的挂起,给完成任务一点额外的时间。完成这项任务后,应用必须调用endBackgroundTask: 方法来让系统知道任务已完成可以被挂起了。
每次调用beginBackgroundTaskWithName:expirationHandler:还是 beginBackgroundTaskWithExpirationHandler:方法都会产生一个与相应任务关联的唯一标记(token)。当你的应用完成任务的时候,它必须使用相应的标记调用 endBackgroundTask:方法,以便让系统知道这个任务完成了。如果为后台任务调用endBackgroundTask:失败将会导致应用终止。如果在开始这个任务的时候你提供一个到器处理器(handler),系统会调用这个处理器,并给你最后一个结束任务以及避免应用终止的机会。
你不需要等到应用进入后台之后再来指定后台任务。更有用的设计是在开始任务之前调用beginBackgroundTaskWithName:expirationHandler:或beginBackgroundTaskWithExpirationHandler:方法,并且一旦你完成就调用endBackgroundTask:方法。你甚至可以在应用还在前台执行的时候遵循这个模式。
代码清单3-1展示了当应用转换到后台的时候如何开始一个长时运行任务。在这个例子中,启动后台任务的请求包含了一个到期处理器,它的任务就是防治任务耗时过长。然后任务自身被提交给调度队列进行异步执行,所以applicationDidEnterBackground:方法能够正常返回。代码块(block)的使用精简了维持对重要变量引用所需的代码,例如后台任务的标识符。清单中的bgTask变量是存储当前后台任务标识符的类的成员变量,并且被此方法使用之前已初始化。
代码清单3-1 在退出的时候启动后台任务
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{
// Clean up any unfinished task business by marking where you
// stopped or ending the task outright.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do the work associated with the task, preferably in chunks.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
注意:当启动任务的时候,总是提供一个到期处理器,但如果你想要知道应用运行了多长时间,请获取UIApplication的backgroundTimeRemaining属性的值。
在你自己的到期处理器中,你可以包含必要的额外代码来关闭你的任务。但是,任何你饱含的代码都不能花费太长时执行时间,因为,在到期处理器被调用的时候,应用已经非常接近它的限定时间了。因此,仅执行最少的状态信息清理,并结束任务。
在后台下载内容
当下载文件时,应用应该使用NSURLSession对象来启动下载,这样系统能够控制下载过程,以防应用被挂起或终止。当你为后台传输配置一个NSURLSession对象时,系统在独立的进程里管理这些传输,并且通过常规方式返回状态报告给应用。如果应用在传输过程中被终止,系统会在后台继续这个传输,并在传输结束或者一个及多个任务需要应用注意的时候启动应用(以合适的方式)。
为了支持后台传输,你必须恰当地配置你的NSURLSession对象。要配置会话,你必须先创建一个NSURLSessionConfiguration对象,并将几个属性设置恰当地值。然后,在你创建会话的时候,把配置好的对象传递给适当地NSURLSession初始化方法。
创建支持后台下载的配置对象的过程如下所示:
- 使用NSURLSessionConfiguration的backgroundSessionConfigurationWithIdentifier:方法来创建配置对象。
- 将配置对象的sessionSendsLaunchEvents属性设置为YES。
- 如果应用在前台启动了传输,建议将配置对象的discretionary属性也设置为YES。
- 恰当地配置其他配置对象的其他属性。
- 使用这个配置对象创建NSURLSession对象。
一旦配置完成,你的NSURLSession对象会无缝地在适当的时候将上传和下载的任务交给系统。如果在应用还在运行的时候任务完成(无论是前台还是后台),会话对象都会以正常方式通知它的委托对象。如果任务还没有完成而系统终止了应用,系统会自动的在后台继续管理这些任务。如果用户终止了应用,则系统就会取消任何等待中的任务。
当所有与后台会话关联的任务完成的时候,系统重启终止的应用(假定sessionSendsLaunchEvents属性被设置为YES,并且用户没有强退应用),并且调用应用的委托对象的application:handleEventsForBackgroundURLSession:completionHandler:方法。(系统还可以重启应用来处理验证,或者其他需要应用注意的任务相关的事件)。在你的委托方法实现中,使用提供的标识符创建新的与之前配置相同的NSURLSessionConfiguration和NSURLSession对象。系统会将你的心会话对象连接到之前的任务,并将它们的状态报告给会话对象的委托对象。
实现长时运行任务
为了实现那些请求了更多执行时间的任务,你必须请求特定的许可来让他们在后台运行而不被挂起。在iOS中,只有特定的几种应用类型才被允许在后台运行:
- 在后台时向用户播放音频内容的应用,例如音乐播放应用。
- 在后台时录制音频内容的应用。
- 随时记录用户位置信息的应用,例如导航应用。
- 支持互联网语音协议(Voice over Internet Protocol,VoIP)的应用。
- 需要经常下载和处理新内容的应用。
- 从外部配件接收定期更新的应用
实现这些服务的应用必须声明它们支持这些服务,并且使用系统框架来实现这些服务的相关方面。声明服务让系统知道你使用了哪个服务,但在某些情况下,事实上阻止你的应用被挂起的是系统框架。
声明应用支持的后台任务
那些后台执行的类的支持,必须通过使用它们的类应用提前声明。从Xcode 5开始,从项目设置的Capabilities选项卡中声明你的应用支持的后台模式。启用Background Modes选项将UIBackgroundModes键添加到到应用的Info.plist文件。选择一个或多个复选框来添加相应的后台模式值到那个键。表3-1罗列了你能指定的后台模式,以及Xcode分配给应用Info.plist 文件中的UIBackgroundModes键的值。
表3-1 应用的后台模式
Xcode后台模式 | UIBackgroundModes值 | 描述 |
---|---|---|
Audio and AirPlay | audio | 应用向用户播放声音内容,或者在后台录制音频。(这个内容包含使用AirPlay的音频流或视频流。) 用户必须在第一次使用前允许应用使用麦克风。更多信息,参见Supporting User Privacy。 |
Location update | location | 应用允许保存用户的位置信息,甚至当应用运行在后台的时候。 |
Voice over IP | voip | 应用提供一种让用户使用网络连接打电话的功能。 |
Newsstand downloads | newsstand-content | 应用是Newsstand应用,它在后台下载并处理杂志或报纸的内容。 |
External accessory communication | external-accessory | 应用与需要通过External Accessory框架提供定期更新的硬件附件配合使用。 |
Uses Bluetooth LE accessories | bluetooth-central | 应用与需要通过Core Bluetooth 框架提供定期更新的蓝牙附件配合使用。 |
Acts as a Bluetooth LE accessory | bluetooth-peripheral | 应用通过Core Bluetooth框架支持在外设模式下的蓝牙通信。 使用这个模式需要用户授权;更多信息参见Supporting User Privacy。 |
Background fetch | fetch | 应用定期从网络下载和处理少量内容。 |
Remote notifications | remote-notification | 应用想要在推送通知到达的时候开始下载内容。使用通知来最小化显示推送通知相关的内容的延时。 |
以上的每种模式让系统知道你的应用在合适的时候会被唤醒或者启动过以响应相关的事件。例如,应用开始播放音乐,然后它进入后台,但仍然需要执行时间来填充音频输出缓冲器。启用Audio模式,告诉系统框架,它们应该继续以适当地间隔来进行必要的调用。如果应用没有选择这个模式,所有通过这个应用正在播放或录制的音频,在应用进入后台的时候都会停止。
(未完待续......)