Background Execution--iOS后台运行机制总结

iOS后台运行机制的探索总结。

为了能够把系统资源合理的分配给用户正在使用的前台应用以及改善电池的使用寿命,iOS中,对于许多应用,当应用不在前台运行时,系统会将以一种简短的方式将其挂起。(For many apps, the background state is just a brief stop on the way to the app being suspended).

绝大部分应用程序简单的挂起就足够了,但是也有一部分应用以正当的理由在后台继续运行。比如,远足应用需要随时跟踪用户位置,音频应用可能需要后台播放音乐,还有一些应用需要后台下载,以便零延迟的向用户呈现内容。
当你认为你的应用程序需要在后台运行,iOS提供的技术可以高效的帮你完成。其实现大致分为以下三种:

  1. 应用前台时,开启短任务(short task)。

  2. 将下载任务交给系统处理,从而使程序被暂停或终止时,下载任务仍然继续。

  3. 特定类型任务,应用可以申报一个或多个后台运行模式(background execution modes.)。

Always try to avoid doing any background work unless doing so improves the overall user experience. An app might move to the background because the user launched a different app or because the user locked the device and is not using it right now. In both situations, the user is signaling that your app does not need to be doing any meaningful work right now. Continuing to run in such conditions will only drain the device’s battery and might lead the user to force quit your app altogether. So be mindful about the work you do in the background and avoid it when you can.

执行有限长度的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)applicationDidEnterBackground:(UIApplication *)application
{
__block UIBackgroundTaskIdentifier bgTask;
bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{
// 处理未完成的任务
// 结束任务.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];

// 异步执行,保证使用耗时任务时能够及时调用 endBackgroundTask: 方法
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 耗时任务

[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}

由于时间的限制,只能执行有限时长的任务。

后台下载

为了支持后台传输,您必须配置NSURLSession适当的对象。要配置会话,必须先创建一个NSURLSessionConfiguration对象,并设置多个属性为适当的值。然后,该配置对象传递给适当的初始化方法NSURLSession创建会话时。

创建一处后台下载的任务方法:

  1. 使用backgroundSessionConfigurationWithIdentifier:创建NSURLSessionConfiguration配置对象 .
  2. 设置配置对象的sessionSendsLaunchEvents属性为YES。
  3. 大文件时推荐把discretionary属性设为YES
  4. 适当的配置其他属性。
  5. 使用配置好的NSURLSessionConfiguration对象来创建你的NSURLSession对象。

执行长时间运行的任务

在iOS中,只有特定类型的应用程序允许在后台运行:

  1. 后台播放音频的软件,如音乐播放器。
  2. 后台录制音频的软件。
  3. 是用户在任何时候知晓自己的位置,如导航应用。
  4. VoIP应用,如QQ电话。
  5. 需要定期下载和更新内容的应用。
  6. 定期从外部附件收到更新的应用。
Xcode background mode UIBackgroundModes value 描述
Audio and AirPlay audio 后台播放或录制音频
Loaction updates location 实时告知用户自己的位置
Voice over IP voip 网络电话
Newsstand downloads newsstand-content 后台下载杂志或报纸内容
External accessory communication external accessory 外部附件
Uses Bluetooth LE accessories bluetooth-central 蓝牙
Acts as a Bluetooth LE accessory bluetooth-periphral 蓝牙
Background fetch fetch 定期下载少了来自网络的内容
Remote-notification remote-notification 收到推送时,下载相关内容。

跟踪用户位置

有以下三种方式在后台跟踪用户的位置:

  1. 位置发生显著的变化服务(significant-change location service)。(推荐)
  2. 前台定位服务(Foreground-only location services)。
  3. 后台定位服务(Background location services)。

显著的位置变化服务:基站定位,位置发生大的改变时唤醒应用在后台执行更新。
前台定位服务只有在前台时才可以获取到用户的位置信息。
后台定位服务不能阻止系统挂起应用,但是有新的位置数据更新时,能够唤醒应用执行任务。

《Location and Maps Programming Guide》

播放和录制背景声音

当UIBackgroundModes项包含audio值,播放或录制音频文件时,应用将不能被暂停,但是播放或录制停止时,系统将挂起应用。
注:早期为了后台运行,会弄一个无声音的音频文件不停播放。现在被发现被拒。

《Audio Session Programming Guide》

构建VoIP应用

《Tips for Developing a VoIP App》

不失时机地获取少量的内容

需要在Info.plist 文件中 UIBackgroundModes项增加 fetch 类型。系统将在最佳时机唤醒或启动应用到后台,调用application:performFetchWithCompletionHandler:方法,使用该方法来检查新的内容,有内容时启动下载操作。
建议使用NSURLSession类来启动和管理您的下载,有关如何使用这个类来管理上传和下载任务的信息,请参阅《URL Session Programming Guide》

使用推送通知来启动下载

需要在 Info.plist 文件中 UIBackgroundModes 项增加 remote-notification 类型,远程通知payload中必须包含 content-available key,而且,value 为 1。
满足以上条件,收到通知时 系统将唤醒后台的程序 ,并调用 delegateapplication:didReceiveRemoteNotification:fetchCompletionHandler:

后台下载杂志

《Newsstand Kit Framework Reference》.

连接外部附件

任何支持连接外部附件的后台处理更新的应用程序必须遵循几个基本原则:

  • 应用必须提供一个接口,它允许用户启动和停止附件更新事件的传输。该接口应该在合适的时候打开或关闭。
  • 一旦被唤醒,应用程序有10秒左右来处理数据。理想的情况下,就应该尽可能快地处理数据,并允许自身被再次挂起。如果需要更多的时间,有必要的情况下,应用程序可以使用该beginBackgroundTaskWithExpirationHandler:方法来请求额外的时间。

连接蓝牙外设

《Core Bluetooth Programming Guide》.

在后台运行时获取用户的关注

本地通知示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void)scheduleAlarmForDate:(NSDate*)theDate
{
UIApplication* app = [UIApplication sharedApplication];
NSArray* oldNotifications = [app scheduledLocalNotifications];

// Clear out the old notification before scheduling a new one.
if ([oldNotifications count] > 0)
[app cancelAllLocalNotifications];

// Create a new notification.
UILocalNotification* alarm = [[UILocalNotification alloc] init];
if (alarm)
{
alarm.fireDate = theDate;
alarm.timeZone = [NSTimeZone defaultTimeZone];
alarm.repeatInterval = 0;
alarm.soundName = @"alarmsound.caf";
alarm.alertBody = @"Time to wake up!";

[app scheduleLocalNotification:alarm];
}
}

您可以取消计划的通知或获得通知的清单。有关这些方法的详细信息,请参阅《UIApplication Class Reference》。有关配置本地通知的更多信息,请参阅《Local and Remote Notification Programming Guide》

做一个负责的后台应用

当涉及到利用系统资源和硬件,前台应用始终拥有高于后台应用程序的优先级。在后台运行的应用程序需要对这种差异有所准备,并调整自己的行为。具体来说,应用在后台时应遵循以下原则:

  • 不要做任何的OpenGL ES代码调用。
  • 被暂停之前取消所有的Bonjour相关的服务。
  • 准备处理基于sockets 的 连接错误。
  • 进入到后台之前保存您的应用程序的状态。参阅《Preserving Your App’s Visual Appearance Across Launches》
  • 移动到后台时删除对不需要的对象的强引用。
  • 停止使用共享系统资源。如地址簿,日历数据库。如果在后台使用这些戏院,程序将被系统杀死。
  • 避免刷新windows和views.
  • 响应连接和断开了外部附件的通知.《External Accessory Programming Topics》.
  • 清理主动警告的视图。系统不会自动dismiss UIActionSheetUIAlertView ,进入后台之前,你可能需要做一些清理工作。
  • 进入后台之前隐藏敏感信息。如密码等,应该在applicationDidEnterBackground:之前隐藏,防止被后台快照捕捉到。
  • 考虑到诸多限制,在后台运行时,应该做最少的工作。

禁止后台运行

如果你希望你的程序任何时候都不要在后台运行,可以在程序的Info.plist中 设置 UIApplicationExitsOnSuspendkey,并将值设置YES
此时如果用户按下Home键,delegate的 applicationWillTerminate:方法将被调用,大约五秒后,程序将被终止。
使用场景比较小。如果后台执行显著增加了程序的复杂性,关闭后台运行也许是一个简单的方案。另外,如果程序需要消耗大量的内存,而且不能轻易的释放任何信息,系统可能会迅速杀死你的应用为其他应用让路,这种情况下选择禁止后台运行,会产生相同的结果,并节省开发时间和精力。

相关资料