在 iOS 上执行后台处理的最简单方法是将后台要求分解为任务,然后在后台运行任务。 任务具有严格的时间限制,在应用程序移动到 iOS 6 上的后台后,处理时间通常大约为 600 秒(10 分钟),在 iOS 7+ 上则为不到 10 分钟。
后台任务可以分为三类:
- 后台安全任务 - 当应用程序进入后台时,在应用程序中的任意位置调用你不希望中断的任务。
- DidEnterBackground 任务 - 在
DidEnterBackground
应用程序生命周期方法期间调用,以帮助清理和状态保存。 - 后台传输 (iOS 7+) - 用于在 iOS 7 上执行网络传输的特殊类型的后台任务。 与常规任务不同,后台传输没有预先确定的时间限制。
在 iOS 6 和 iOS 7 上使用后台安全和 DidEnterBackground
任务是安全的,但有一些细微的差异。 让我们更详细地调查这两种类型的任务。
创建后台安全任务
某些应用程序包含不应被 iOS 中断的任务(如果应用程序更改状态)。 防止这些任务中断的一种方法是将它们作为长时间运行的任务注册到 iOS。 如果用户将应用置于后台,则可以在应用程序中不希望任务中断的任何位置使用此模式。 此模式的候选项是向服务器发送新用户的注册信息或验证登录信息等任务。
以下代码片段演示如何注册在后台运行的任务:
nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});
//runs on main or background thread
FinishLongRunningTask(taskID);
UIApplication.SharedApplication.EndBackgroundTask(taskID);
注册过程将任务与唯一标识符配对,taskID
,然后将其包装在匹配的 BeginBackgroundTask
和 EndBackgroundTask
调用中。 为了生成标识符,我们对 UIApplication
对象上的 BeginBackgroundTask
方法进行调用,然后启动长时间运行的任务(通常是在新线程上)。 任务完成后,我们将调用 EndBackgroundTask
并传入同一标识符。 这一点很重要,因为如果 BeginBackgroundTask
调用没有匹配的 EndBackgroundTask
,iOS 将终止应用程序。
重要
后台安全任务可以在主线程或后台线程上运行,具体取决于应用程序的需求。
在 DidEnterBackground 期间执行任务
除了使长时间运行的任务后台安全外,注册还可用于在应用程序进入后台时启动任务。 iOS 在 AppDelegate 类中提供了一种事件方法,该类称为 DidEnterBackground
,可用于在应用程序进入后台之前保存应用程序状态、保存用户数据和加密敏感内容。 应用程序有大约五秒钟的时间从此方法返回,否则它将终止。 因此,可以从 DidEnterBackground
方法内部调用可能需要 5 秒以上的清理任务。 必须在单独的线程上调用这些任务。
此过程与注册长时间运行的任务的过程几乎相同。 以下代码片段演示了此操作:
public override void DidEnterBackground (UIApplication application) {
nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});
new Task ( () => {
DoWork();
UIApplication.SharedApplication.EndBackgroundTask(taskID);
}).Start();
}
首先,在 AppDelegate
中重写 DidEnterBackground
方法,在该方法中,我们通过 BeginBackgroundTask
注册任务,就像在前面的示例中一样。 接下来,我们会生成一个新线程并执行长时间运行的任务。 请注意,EndBackgroundTask
调用现在是从长时间运行的任务内部进行的,因为已返回 DidEnterBackground
方法。
重要
iOS 使用监视器机制来确保应用程序的 UI 保持响应。 在 DidEnterBackground
花费太多时间的应用程序将在 UI 中变得无响应。 启动在后台运行的任务可让 DidEnterBackground
及时返回,使 UI 保持响应并阻止监视程序终止应用程序。
处理后台任务时间限制
iOS 对后台任务可以运行多长时间施加了严格限制,如果 EndBackgroundTask
调用未在分配的时间内进行,则该应用程序将终止。 通过跟踪剩余的后台时间,并在必要时使用过期处理程序,可以避免 iOS 终止应用程序。
访问后台剩余时间
如果具有已注册任务的应用程序移动到后台,则注册的任务将有大约 600 秒进行运行。 我们可以检查任务使用 UIApplication
类的静态 BackgroundTimeRemaining
属性完成的时间。 以下代码将为后台任务留下的时间(以秒为单位):
double timeRemaining = UIApplication.SharedApplication.BackgroundTimeRemaining;
避免使用过期处理程序终止应用
除了授予对 BackgroundTimeRemaining
属性的访问权限之外,iOS 还提供一种正常的方式来通过过期处理程序处理后台时间过期。 这是一个可选的代码块,将在任务分配的时间即将过期时执行。 过期处理程序中的代码会调用 EndBackgroundTask
并传入任务 ID,这表示应用表现良好,并且阻止 iOS 终止应用,即使任务超时也是如此。 必须在过期处理程序中调用 EndBackgroundTask
,并在正常执行过程中也进行调用。
过期处理程序使用 lambda 表达式表示为匿名函数,如下所示:
Task.Factory.StartNew( () => {
//expirationHandler only called if background time allowed exceeded
var taskId = UIApplication.SharedApplication.BeginBackgroundTask(() => {
Console.WriteLine("Exhausted time");
UIApplication.SharedApplication.EndBackgroundTask(taskId);
});
while(myFlag == true)
{
Console.WriteLine(UIApplication.SharedApplication.BackgroundTimeRemaining);
myFlag = SomeCalculationNeedsMoreTime();
}
//Only called if loop terminated due to myFlag and not expiration of time
UIApplication.SharedApplication.EndBackgroundTask(taskId);
});
虽然运行代码不需要过期处理程序,但应始终将过期处理程序与后台任务一起使用。
iOS 7+ 中的后台任务
iOS 7 在后台任务方面的最大变化不是任务的执行方式,而是任务的运行时间。
回想一下,在 iOS 7 之前,后台运行的任务有 600 秒来完成。 此限制的一个原因是,后台运行的任务会让设备保持唤醒状态直到任务完成。
iOS 7 后台处理经过优化,可延长电池使用时间。 在 iOS 7 中,后台处理变得机会主义化:不会再让设备保持唤醒状态,任务会在设备进入休眠时停止,并在设备唤醒以处理电话、通知、收到的电子邮件和其他常见干扰时分块进行处理。 下图提供有关任务可能如何分解的见解:
由于任务运行时不再持续,因此必须在 iOS 7 中以不同的方式处理执行网络传输的任务。 鼓励开发人员使用 NSURlSession
API 来处理网络传输。 下一部分将概述后台传输。
后台传输
iOS 7 中后台传输的支柱是新的 NSURLSession
API。 NSURLSession
允许我们创建任务来:
- 通过网络和设备中断传输内容。
- 上传和下载大型文件(后台传输服务)。
让我们详细了解一下它的工作原理。
NSURLSession API
NSURLSession
是一个功能强大的 API,用于通过网络传输内容。 它提供了一组工具,用于处理通过网络中断和应用程序状态更改传输数据。
NSURLSession
API 会创建一个或多个会话,从而生成任务以跨网络传送相关数据的块。 任务以异步方式运行,以便快速可靠地传输数据。 由于 NSURLSession
是异步的,因此每个会话都需要完成处理程序块,以让系统和应用程序知道传输何时完成。
若要执行同时在 iOS 7 前和 iOS 7 后上有效的网络传输,请检查 NSURLSession
是否可用于排队传输,如果不可用,则使用常规后台任务执行传输:
if ([NSURLSession class]) {
// Create a background session and enqueue transfers
}
else {
// Start a background task and transfer directly
// Do NOT make calls to update the UI here!
}
重要
避免调用 iOS 6 兼容代码中的后台更新 UI,因为 iOS 6 不支持后台 UI 更新,并将终止应用程序。
NSURLSession
API 包括一组丰富的功能,用于处理身份验证、管理失败的传输,以及报告客户端(而不是服务器端)错误。 它有助于弥合 iOS 7 中引入的任务运行时中断,并支持快速可靠地传输大型文件。 下一部分将探讨第二项功能。
后台传输服务
在 iOS 7 之前,上传或下载后台的文件不可靠。 后台任务运行时间有限,但传输文件所需的时间因网络和文件大小而异。 在 iOS 7 中,可以使用 NSURLSession
成功上传和下载大型文件。 处理后台大型文件网络传输的特定 NSURLSession
会话类型称为 后台传输服务。
使用后台传输服务启动的传输由操作系统管理,并提供用于处理身份验证和错误的 API。 由于传输不受任意时间限制的约束,因此可用于上传或下载大型文件、后台自动更新内容等。 有关如何实现服务的详细信息,请参阅后台传输演练。
后台传输服务通常与后台提取或远程通知配对,以帮助应用程序在后台刷新内容。 在接下来的两部分中,我们将介绍注册整个应用程序以在 iOS 6 和 iOS 7 上后台运行的概念。