后台执行 Background Execution

2018-06-20  本文已影响0人  微笑中的你

当用户没有积极地使用你的应用程序时,系统将它移动到后台状态。对于许多应用程序来说,后台状态只是暂停应用程序的一个短暂停留。暂停应用程序是提高电池寿命的一种方式,它还允许系统将重要的系统资源投入到吸引用户注意力的新前景应用中。

大多数应用程序可以很容易地移动到悬浮状态,但也有合法的理由让应用程序继续在后台运行。一个徒步旅行应用程序可能想要跟踪用户的位置,以便它能显示在徒步地图上覆盖的路线。一个音频应用程序可能需要在锁屏上继续播放音乐。其他的应用程序可能想要在后台下载内容,这样就可以最小化将内容呈现给用户的延迟。当你发现有必要让你的应用程序在后台运行时,iOS会帮助你高效地做到这一点,而不会消耗系统资源或用户的电池。iOS提供的技术分为三类:

总是尽量避免做任何背景工作,除非这样做能改善整体的用户体验。一个应用程序可能会移动到后台,因为用户启动了一个不同的应用程序,或者因为用户锁定了设备,而现在不使用它。在这两种情况下,用户都在暗示你的应用程序现在不需要做任何有意义的工作。继续在这样的条件下运行只会耗尽设备的电池,并可能导致用户强制退出你的应用程序。所以要注意你在后台所做的工作,尽量避免。

执行有限长度的任务

移动到后台的应用程序将会尽可能快地进入静止状态,这样它们就可以被系统挂起。如果您的应用程序处于任务的中间,并且需要一点额外的时间来完成该任务,它可以调用beginBackgroundTaskWithName:expirationHandler:或beginBackgroundTaskWithExpirationHandler: UIApplication对象的方法来请求额外的执行时间。调用这两种方法可以暂时延缓应用程序的暂停,让它有一点额外的时间来完成它的工作。完成这项工作后,你的应用程序必须调用endBackgroundTask:方法让系统知道它已经完成并且可以挂起。

每个调用beginBackgroundTaskWithName:expirationHandler:或beginBackgroundTaskWithExpirationHandler:方法生成一个与相应任务相关联的唯一令牌。当应用程序完成一个任务时,它必须调用endBackgroundTask:方法与相应的令牌,让系统知道任务已经完成。没有调用endBackgroundTask:后台任务的方法将导致应用程序的终止。如果您在启动任务时提供了一个过期处理程序,系统将调用该处理程序,并给您最后一次机会来结束任务并避免终止。

你不需要等到你的应用程序移动到后台去指定后台任务。一个更有用的设计是调用beginBackgroundTaskWithName:expirationHandler:或者beginBackgroundTaskWithExpirationHandler:方法在启动任务之前,并调用endBackgroundTask:方法在完成之后。您甚至可以在应用程序在前台执行时遵循此模式。

清单3-1展示了如何在应用程序转换到后台时启动长时间运行的任务。在本例中,启动后台任务的请求包含一个过期处理程序,以防任务花费的时间太长。然后,任务本身被提交到一个调度队列,用于异步执行,以便applicationDidEnterBackground:方法可以正常返回。块的使用简化了维护对任何重要变量(如后台任务标识符)的引用所需的代码。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的适当初始化方法。

创建支持后台下载的配置对象的过程如下:

  1. 使用NSURLSessionConfiguration的方法[backgroundSessionConfigurationWithIdentifier:] 来创建configuration 对象.

  2. 将配置对象的 [sessionSendsLaunchEvents] 属性的值设置为YES。

  3. 如果您的应用程序在前台中开始传输,那么建议您将配置对象的 [discretionary]属性设置为YES。

  4. 适当地配置configuration对象的任何其他属性

  5. 使用 configuration 对象来创建 NSURLSession 对象.

一旦配置好,NSURLSession对象会在适当的时候无缝地将上传和下载任务交给系统。如果任务在应用程序仍在运行时完成(无论是在前台还是后台),会话对象将以通常的方式通知它的委托。如果任务尚未完成,系统终止您的应用程序,系统将自动继续在后台管理任务。如果用户终止您的应用程序,系统将取消任何挂起的任务。

当与后台会话相关的所有任务完成后,系统重新启动一个终止的应用程序(假设sessionSendsLaunchEvents属性被设置为YES,并且用户没有强制退出应用程序),并调用应用程序委托的应用程序:handleeventsforbackgrounlsession:completionHandler: method。(系统还可能重新启动应用程序,以处理身份验证挑战或其他需要应用程序关注的与任务相关的事件。)在实现该委托方法时,使用提供的标识符创建一个新的NSURLSessionConfiguration和NSURLSession对象,其配置与前面相同。系统将新会话对象重新连接到以前的任务,并将它们的状态报告给会话对象的委托。

实现长时间运行的任务

对于需要更多执行时间来实现的任务,您必须请求特定的权限在后台运行它们,而不需要挂起它们。在iOS中,只有特定的app类型可以在后台运行:

实现这些服务的应用程序必须声明它们支持的服务,并使用系统框架来实现这些服务的相关方面。声明服务使系统知道您使用了哪些服务,但是在某些情况下,实际上是系统框架阻止了您的应用程序被挂起。

声明应用程序支持的后台任务

对某些类型的后台执行的支持必须在应用程序预先声明。在Xcode 5和更高版本中,您可以从项目设置的capability选项卡中声明应用程序支持的后台模式。启用后台模式选项将UIBackgroundModes 键添加到应用程序的info.plist文件。选择一个或多个复选框将为该键添加相应的背景模式值。表3-1列出了可以指定的后台模式UIBackgroundModes 的键值

表3-1

backgroundMode value description
Audio and AirPlay audio 该应用程序在后台播放声音内容,或者记录音频。(该内容包括播放音频或视频内容。)
用户必须在第一次使用前批准应用程序使用麦克风;有关更多信息,请参见支持用户隐私。
Location 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-peripheral 通过核心蓝牙框架,支持外围模式蓝牙通信。
使用此模式需要用户授权;有关更多信息,请参见支持用户隐私。
Background fetch fetch 该应用程序定期从网络下载和处理少量内容。
Remote notifications remote-notification 该应用程序希望在推送通知到达时开始下载内容。使用此通知将显示与推送通知相关的内容的延迟最小化。

前面的每一种模式都让系统知道,应用程序应该在适当的时候唤醒或启动,以响应相关事件。例如,一个开始播放音乐然后移动到后台的应用程序仍然需要执行时间来填充音频输出缓冲区。启用音频模式告诉系统框架,它们应该继续以适当的间隔对应用程序进行必要的回调。如果应用程序没有选择这种模式,当应用程序移动到后台时,应用程序播放或录制的任何音频都会停止。

跟踪用户的位置

有几种方法可以跟踪用户在后台的位置,其中大部分并不需要您的应用程序在后台持续运行:

对于不需要高精度位置数据的应用程序,强烈推荐具有重大变化的位置服务。使用此服务,只有当用户的位置发生显著变化时才生成位置更新;因此,它非常适合为用户提供非关键的、与位置相关的信息的社交应用程序或应用程序。如果应用程序在更新发生时被挂起,系统会在后台唤醒它来处理更新。如果应用程序启动该服务并终止,系统将在新位置可用时自动重新启动该应用程序。该服务可在ios4和以后版本中使用,并且只能在包含蜂窝收音机的设备上使用。

前台和后台位置服务都使用标准位置核心位置服务来检索位置数据。唯一的区别是,只有前台的位置服务在应用程序暂停时停止更新,如果应用程序不支持其他后台服务或任务,这很可能发生。只在前台提供定位服务的应用程序只需要在前台提供位置数据。

您可以从Xcode项目中的capability选项卡的后台模式部分启用位置支持。(您还可以通过在应用程序的Info.plist文件中包含带有位置值的uibackgroundmode键来启用这种支持。)启用此模式并不会阻止系统挂起应用程序,但它会告诉系统,只要有新的位置数据要发送,就应该唤醒应用程序。因此,这个键可以有效地让应用程序在后台运行,以便在发生位置更新时进行处理。

重要提示:鼓励您谨慎使用标准服务,或者使用重要的位置更改服务。定位服务需要积极使用iOS设备的车载无线电硬件。连续运行这个硬件可以消耗大量的电力。如果您的应用程序不需要向用户提供精确和连续的位置信息,那么最好将位置服务的使用最小化。

有关如何在应用程序中使用不同位置服务的信息,请参阅 位置和地图编程指南

后台播放和录制音频

一个连续播放或记录音频的应用程序(即使是在后台运行的应用程序)可以注册以在后台执行这些任务。在Xcode项目中,可以从“功能”选项卡的“后台模式”部分启用音频支持。(您还可以通过在应用程序的Info.plist文件中包含带有音频值的uibackgroundmode键来启用这种支持。)在后台播放音频内容的应用程序必须播放音频内容,而不是静默。

后台音频应用的典型例子包括:

当uibackgroundmode键包含音频值时,系统的媒体框架会自动阻止相应的应用程序在移动到后台时被挂起。只要播放音频或视频内容或录制音频内容,应用程序就会继续在后台运行。但是,如果录制或回放停止,系统会挂起应用程序。

您可以使用任何系统音频框架来处理后台音频内容,并且使用这些框架的过程是不变的。(播放视频时,可以使用媒体播放器或AV Foundation框架来呈现视频。)因为你的应用程序在播放媒体文件时不会被暂停,所以当你的应用程序在后台时,回调会正常运行。但是,在回调中,您应该只做为回放提供数据所需的工作。例如,流媒体音频应用程序需要从其服务器下载音乐流数据,并将当前的音频样本推出以进行回放。应用程序不应该执行任何与回放无关的额外任务。

因为不止一个应用程序可以支持音频,系统决定哪个应用程序可以在任何给定的时间播放或录制音频。前台应用程序总是优先进行音频操作。可以允许多个后台应用程序播放音频,这些决定基于每个应用程序的音频会话对象的配置。您应该始终适当地配置应用程序的音频会话对象,并小心地使用系统框架处理中断和其他类型的音频相关通知。有关如何配置用于后台执行的音频会话对象的信息,请参阅 Audio Session Programming Guide

实现一个网络电话应用程序

通过网络协议(VoIP)的语音应用,用户可以通过网络连接而不是手机的移动服务打电话。这样的应用程序需要维护与其关联的服务的持久网络连接,以便能够接收到的呼叫和其他相关数据。该系统不让VoIP应用程序一直处于清醒状态,而是允许它们被挂起,并为它们提供监视套接字的设施。当检测到传入的流量时,系统唤醒VoIP应用程序并将其套接字的控制权返回给它。

要配置VoIP应用程序,您必须完成以下操作:

  1. 在您的Xcode项目中,从功能选项卡的后台模式部分支持对IP语音的支持。(您还可以通过在应用程序的Info.plist文件中包含带有voip值的uibackgroundmode键来启用这种支持。)

  2. 为应用程序的VoIP使用配置一个套接字.

  3. 在移动到后台之前,调用setKeepAliveTimeout:handler:方法来安装一个定期执行的处理程序。您的应用程序可以使用这个处理程序来维护它的服务连接。

  4. 配置您的音频会话以处理到活动使用的转换。

在uibackgroundmode键中包含voip值让系统知道,它应该允许应用程序在后台运行,以管理其网络套接字。系统启动后,带有此密钥的应用程序也会立即在后台重新启动,以确保VoIP服务始终可用。

大多数VoIP应用程序也需要配置为后台音频应用程序,以便在后台传输音频。因此,您应该将 audio 和 voip 值都包含到backgroundmode键中。如果你不这样做,你的应用程序就不能在后台播放或录制音频。

适时地获取少量内容

需要定期检查新内容的应用程序可以要求系统唤醒它们,这样它们就可以启动该内容的获取操作。要支持这种模式,请在Xcode项目中启用“功能”选项卡的“后台模式”选项。(您还可以通过在应用程序的Info.plist文件中包含带有fetch值的uibackgroundmode键来启用这种支持。)启用这种模式并不能保证系统会给你的应用程序任何时间来执行后台取回。系统必须平衡应用程序获取内容的需要和其他应用程序和系统本身的需要。在对这些信息进行评估之后,系统会在有很好的时机为应用程序提供时间。

当一个好的机会出现时,系统会唤醒或启动你的应用程序到后台,并调用应用程序委托的 application:performFetchWithCompletionHandler:方法。使用该方法检查新内容,如果内容可用,则启动下载操作。下载完新内容后,必须执行所提供的完成处理程序块,传递一个结果,指示内容是否可用。执行这个block告诉系统它可以将你的应用程序移回到暂停状态并评估它的电力使用情况。应用程序下载少量的内容迅速和准确地反映内容下载,将来更有可能收到执行时间比应用程序需要很长时间才能下载内容或声称内容然后不要下载任何东西。

当下载任何内容时,建议使用NSURLSession类来启动和管理下载。有关如何使用这个类来管理上传和下载任务的信息,请参阅URL加载系统编程指南(URL Loading System Programming Guide)。这个没有连接地址!!!

使用推送通知启动下载

如果你的服务器在你的应用有新内容时向用户的设备发送推送通知,你可以要求系统在后台运行你的应用,这样它就可以立即开始下载新内容。这种后台模式的目的是最小化用户看到推送通知和应用程序能够显示相关内容之间的时间间隔。应用程序通常会在用户看到通知的同时被唤醒,但这仍然会给你更多的时间。

要支持此后台模式,请在Xcode项目中的capability选项卡的后台模式部分中启用远程通知选项。(您还可以通过在应用程序的Info.plist文件中包含带有 remote-notification 值的uibackgroundmode键来启用这种支持。)

要触发下载操作的push通知,通知的有效负载必须包含内容可用的密钥,其值设置为1。当这个键出现时,系统会在后台唤醒应用程序(或启动到后台),并调用应用程序委托的应用程序:didreceivertenotification:fetchCompletionHandler:方法。该方法的实现应该下载相关内容并将其集成到应用程序中。

当下载任何内容时,建议使用NSURLSession类来启动和管理下载。有关如何使用这个类来管理上传和下载任务的信息,请参阅URL加载系统编程指南。

在后台下载新闻台内容

下载新杂志或报纸问题的报摊应用程序可以注册在后台执行这些下载。您可以从Xcode项目中的capability选项卡的后台模式部分支持newsstand下载。(您还可以通过在应用程序的Info。plist文件中包含带有newsstand-content值的uibackgroundmode键来启用这种支持。)当这个键出现时,系统会启动你的应用程序(如果它还没有运行),这样它就可以开始下载新版本了。

当你使用Newsstand Kit框架来启动下载时,系统会为你的应用程序处理下载过程,系统会继续下载文件,即使你的应用程序被暂停或终止。当下载操作完成后,系统会将文件传输到你的app sandbox并通知你的app。如果app没有运行,这个通知会唤醒它,让它有机会处理新下载的文件。如果在下载过程中出现错误,您的应用程序将被类似地唤醒来处理它们。

有关如何使用Newsstand Kit框架下载内容的信息, 请参考 Newsstand Kit Framework Reference.

与外部设备通信

使用外部附件的应用程序可以要求被唤醒,如果附件在应用程序暂停时提供更新。这种支持对于一些定期发送数据的附件非常重要,比如心率监视器。您可以从Xcode项目中的capability选项卡的后台模式部分支持外部附件通信。(您还可以通过在应用程序的Info.plist文件中包含带有external-accessory 值的uibackgroundmode键来启用这种支持。)当您启用此模式时,外部附件框架不会关闭与附件的活动会话。(在iOS 4和更早的版本中,当应用程序暂停时,这些会话将自动关闭。)当来自附件的新数据到达时,框架将唤醒应用程序,以便它能够处理这些数据。系统还会唤醒应用程序来处理附属连接和断开通知。

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

与蓝牙设备进行通信

使用蓝牙外围设备的应用程序可以要求被唤醒,如果外围设备在应用程序挂起时提供更新。这种支持对于蓝牙设备配件来说是很重要的,这些配件可以定时发送数据,比如蓝牙心率腰带。在您的Xcode项目中,支持使用“功能”选项卡的“后台模式”部分中的蓝牙附件。(您还可以通过在应用程序的Info.plist中包含带有 bluetooth-central 值的uibackgroundmode键来启用这种支持文件。)当启用此模式时,核心蓝牙框架将为相应的外围设备打开任何活动会话。此外,来自外设的新数据会导致系统唤醒应用程序,使其能够处理数据。系统还会唤醒应用程序来处理附件连接和断开通知。

在ios6中,应用程序也可以使用蓝牙设备以外围模式运行。要充当蓝牙附件,您必须从Xcode项目中的capability选项卡的后台模式部分启用对该模式的支持。(您还可以通过在应用程序的Info.plist中包含带有bluetooth-peripheral 的uibackgroundmode键来启用这种支持文件。)启用此模式后,核心蓝牙框架可以在后台短暂唤醒应用程序,以便它能够处理与访问相关的请求。为这些事件而醒来的应用程序应该处理它们并尽快返回,以便再次暂停应用程序。

任何支持蓝牙数据后台处理的应用都必须是基于会话的,并遵循以下几个基本原则:

在后台获取用户的注意力

通知是应用程序挂起、在后台运行或不运行以引起用户注意的一种方式。应用程序可以使用本地通知来显示警报、播放声音、标记应用程序的图标,或者将这三种功能结合在一起。例如,一个闹钟应用程序可能会使用本地通知来播放闹铃,并显示一个警报来禁用闹铃。当通知发送给用户时,用户必须决定是否有必要将应用程序带回前台。(如果应用程序已经在前台运行,本地通知会安静地发送给应用程序,而不是用户。)

为了调度本地通知的交付,创建一个UILocalNotification类的实例,配置通知参数,并使用UIApplication类的方法调度它。本地通知对象包含关于要提交的通知类型(声音、警报或徽章)和提交通知的时间(如果适用)的信息。UIApplication类的方法提供了立即或在预定时间交付通知的选项。

清单3-2显示了一个示例,该示例使用用户设置的日期和时间安排单个警报。这个示例每次只配置一个警报,并在调度新警报之前取消之前的警报。(您自己的应用程序在任何给定的时间内都不能有超过128个本地通知激活,其中任何一个都可以配置为在指定的间隔内重复。)警报本身由一个警报框和一个声音文件组成,如果应用程序没有运行或当警报触发时在后台播放。如果应用程序是活动的,因此在前台运行,则调用应用程序委托的应用程序:didReceiveLocalNotification: method。

清单 3-2

- (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];
    }
}

与本地通知使用的声音文件和推送通知的要求相同。自定义声音文件必须位于应用程序的主要包里面支持以下格式之一: Linear PCM, MA4, µ-Law, 或者 a-Law。您还可以指定UILocalNotificationDefaultSoundName常量来播放设备的默认警报声音。当发送通知并播放声音时,系统还会在支持它的设备上触发振动。

可以使用UIApplication类的方法取消计划通知或获取通知列表。有关这些方法的更多信息,请参见UIApplication类引用。有关配置本地通知的其他信息,请参阅 Local and Remote Notification Programming Guide

了解你的应用什么时候启动到后台

支持后台执行的应用程序可以由系统重新启动来处理传入的事件。如果一个应用程序由于除用户强制退出之外的任何原因被终止,系统会在以下事件发生时启动该应用程序:

在大多数情况下,系统不会在用户强制退出后重新启动应用程序。一个例外是位置应用程序,在ios8和之后,它会在用户强制退出后重新启动。不过,在其他情况下,用户必须显式地启动应用程序或重启设备,然后系统才能自动将应用程序启动到后台。当在设备上启用密码保护时,在用户首先解锁设备之前,系统不会在后台启动一个应用程序。

作为一个负责任的后台应用

当使用系统资源和硬件时,前台应用程序总是优先于后台应用程序。在后台运行的应用程序需要为这种差异做好准备,并在后台运行时调整它们的行为。具体来说,移动到后台的应用程序应该遵循以下原则:

如果您正在实现一个后台音频应用程序,或任何其他类型的应用程序,允许在后台运行,那么您的应用程序将以通常的方式响应传入消息。换句话说,当出现低内存警告时,系统可能会通知你的应用程序。在系统需要终止应用程序以释放更多内存的情况下,应用程序会调用其委托的applicationWillTerminate:方法来执行退出前的任何最终任务。

退出后台执行

如果你不想让你的应用程序在后台运行,你可以通过在info.plist 文件中添加UIApplicationExitsOnSuspend键(有值YES)来显式地退出后台。当应用程序选择退出时,它会在非运行状态、非活动状态和活动状态之间循环,不会进入后台或暂停状态。当用户按下Home键退出app时,会调用app委托的applicationWillTerminate:方法,app在终止并恢复到停止运行状态之前,大约有5秒的时间清理和退出。

强烈反对退出后台执行,但在某些情况下可能是首选。具体来说,如果为后台执行编写代码增加了应用程序的复杂性,那么终止应用程序可能是一个更简单的解决方案。此外,如果你的应用程序占用大量内存,而且不能轻松释放其中的任何内存,那么系统可能会迅速地关闭你的应用程序,为其他应用程序腾出空间。因此,选择终止,而不是切换到后台,可能会产生相同的结果,并为您节省开发时间和精力。

免责声明:以上内容均来自官方文档 iOS App 编程指南,在 有道翻译 的基础上进行修改整理,仅用于个人学习。因个人水平有限,如果存在错误,请读者谅解并指出,非常感谢!
上一篇下一篇

猜你喜欢

热点阅读