iOS开发·背景模式教程:入门

3,196 阅读17分钟

iOS 4 的发布,Apple 于 2010 年开始允许应用程序在后台运行,并从那时起不断发展和改进后台模式。iOS 限制使用后台以改善用户体验并延长电池寿命。您的应用程序可以在特定用例的后台运行的最新位置,包括:播放音频、更新和从服务器获取的内容。

您的任务不属于该类别之一,则后台可能不适合。如果您尝试使用超出警告范围的后台模式来游戏系统,您可能会遇到其应用程序商店中的那些情况。 !

在本后台模式教程中,您将了解您的应用程序可以在后台执行的四件事:

  • 音频播放:允许应用继续在后台播放音频。
  • 接收位置更新:使应用程序在后台接收位置更改。
  • 完成有限长度的关键任务:使应用程序在移动到后台后继续完成关键任务。
  • 后台获取按照iOS系统的执行:后台更新。

入门

在深入研究之前,这里是 iOS 中可用的基本后台模式的快速概览:

  • 音频、AirPlay:在应用程序后台支持和画中画时播放音频和视频。
  • 位置更新:在后台继续接收位置更新。
  • IP:通过互联网发送和接收语音。
  • 外部附件通讯:通过闪电端口与外部附件通讯。
  • 使用蓝牙 LE 配件:在后台与 LE 配件通信。
  • 应用蓝牙 LE 配件:允许提供蓝牙 LE 配件信息。
  • 后台获取:执行数据挖掘。
  • 通知:发送和接收远程远程通知。
  • 后台处理:执行的关键过程。

您将在示例应用程序中添加上述上述四种模式以及后台获取。

注意:获得真实的设备上进行操作的时候,它应该是真实的设备上进行的效果。在此期间,当您忘记某个步骤时,应用程序在后台运行。然后,当您切换到可能的真实设备时,可能根本无法工作。

在物理设备上运行项目之前,您必须设置您的开发团队,如下所示:

选择您的开发团队

四个运行并运行以感受睡眠永不停止的应用程序,因为后台执行它 - 一个不受干扰的项目模式,这是一个项目模式:

先看看 Sleepless 示例应用

您要添加的第一个功能是背景音频。

音频播放

在物理设备上制造和运行时不眠不休。到音频选项卡,播放音乐,然后通过返回主屏幕将导航应用程序显示后台。音乐将停止播放。

打开AudioModel.swift 。

该应用程序利用研究观察曲目AVQueuePlayer并顺序播放。该模型玩家的currentItem价值以提供对视图的按更新。

在报价时

启动项目包括来自流行音乐网站的免费作品。com的音频文件。您可以通过名称免费使用音乐。这三首歌都是凯文麦克劳德的:

感谢美好的音乐,凯文!

注意:查看文档Apple的UIKit中应用程序的执行状态,以了解活动状态和其他内容的更多相关信息。

在后台测试音频

应用进入后台时音乐停止?好吧,还有一个关键的部分!

让您启用指示应用程序运行的特定功能,否则大部分后台应用程序都可以在后台执行任务,从而实现任何关键功能。

之后,音频会通知iOS 继续是音频,即使——自动应用程序的后台播放模式也是你的背景激活。几乎是你需要激活它。

返回 Xcode 并执行以下操作:

  1. 点击项目导航器中的项目。
  2. 选择SleeplessApp目标。
  3. 选择签名和功能选项卡。
  4. 单击 + 符号以添加功能。

中,画背景模式以添加此功能展开背景模式功能,然后启用画幅 、AirPlay和画框以音频背景。

为项目添加音频背景模式

在这样的物理设备上制造和运行应用程序。之前开始一样的音乐,然后应用程序。这次视频将继续。就是简单!

SleeplessApp 在后台播放音乐

如此,您将更新后台继续接收位置,即使应用程序后台管理如此。

接收位置更新

首先,构建并运行应用程序。选择位置选项并点击什么都不会发生,因为你会开始一些重要的步骤。你现在改变它。

启用位置更新

打开LocationModel.swift这是向LocationView提供位置数据的代码。你要做一个简单的改变init(). 替换以下两行:

  mgr.requestWhenInUseAuthorization()
  mgr.allowsBackgroundLocationUpdates =  false

和:

mgr.requestAlwaysAuthorization()
 mgr.allowsBackgroundLocationUpdates =  true

即使应用程序未使用,第一行也会请求位置更新。第二个请求甚至在后台更新。

返回到Signing & Capabilities屏幕并选中 位置更新复选框,让 iOS 知道您的应用想要在后台接收位置更新。

启用位置更新后台模式

除了选中此框外,iOS 还要求您在Info.plist 中设置一个键,向用户解释您需要后台更新的原因。如果您不包括此内容,则位置请求将静默失败。

打开Info.plist并添加Privacy - Location Always and When In Use Usage DescriptionPrivacy - Location When In Use Usage Description的键。然后键入应用程序将在地图上显示您的位置作为两个键的值。

位置更新的使用说明

现在,构建并运行!切换到位置选项卡并点击开始

首次加载时,您会看到您在位置隐私原因中写入的消息。

在使用应用程序时点击允许并在建筑物外或周围散步——尽量不要太分心捕捉口袋妖怪。

跟踪位置的权限

位置更新应该开始出现。如果没有,请再次将应用程序发送到后台以触发始终提示进行位置跟踪。您还可以使用“设置” 应用在隐私 ▸ 定位服务 ▸ 不眠设置中启用“不眠”应用的始终跟踪。

如果您将应用程序发送到后台,您仍会在控制台中看到位置更新。

一段时间后,您应该会看到如下内容:

位置更新作为地图上的图钉

在后台测试定位模式

如果您退出应用程序,您应该会在控制台日志中看到应用程序更新位置。再次打开它以查看地图上的所有图钉,显示您在步行期间去过的地方。

如果您使用的是模拟器,您也可以使用它来模拟运动!查看功能 ▸ 位置菜单:

用于位置跟踪的模拟器选项

轻而易举,对吧?进入第三个选项卡和第三个背景模式!

移至后台完成关键任务

下一个后台模式正式称为Extending Your App's Background Execution Time。多么拗口。任务完成更容易说!

从技术上讲,这根本不是后台模式。您不必声明您的应用在 Capabilities 中使用它。它是一种 API,可让您在应用程序处于后台时在有限的时间内运行任意代码,从而为您提供更多时间来完成关键任务,例如保存数据。

何时使用任务完成

Completion后台模式的一个有效用例是完成一些关键任务,例如保存用户的输入或发布交易。有很多可能性。

由于代码是任意的,您可以使用此 API 执行几乎任何操作:执行冗长的计算、对图像应用过滤器、渲染复杂的 3D 网格 — 随便!你的想象力是极限,只要你记住你只能得到一些时间,而不是无限的时间。稍后,您将设置一个在后台运行的冗长计算,以便了解此 API 的工作原理。

iOS 决定了你的应用程序移到后台后的时间。无法保证您被授予的时间,但您可以随时查看UIApplication.shared.backgroundTimeRemaining. 这将告诉您还剩多少时间。

一般的,基于观察的共识是你得到大约 30 秒。同样,没有任何保证,API 文档甚至没有给出估计值——所以不要依赖这个数字。您可能会得到 5 分钟或 5 秒的时间,因此您的应用需要为中断做好准备。当您的时间快到时,iOS 会向您发出回调信号。

设置完成任务

这是每个计算机科学专业的学生都应该熟悉的一项常见任务:计算斐波那契数列中的数字。这里的转折是,您将在应用程序移至后台后计算这些数字!

打开CompleteTaskModel.swift并查看已经存在的内容。就目前而言,此视图将按顺序计算斐波那契数并显示结果。

如果您现在要在实际设备上暂停应用程序,则计算将停止并在应用程序再次激活时恢复原状。你的任务是创建一个后台任务,这样计算就可以继续运行,直到 iOS 说“时间到了!”

您首先需要将以下内容添加到CompleteTaskModel

var backgroundTask: UIBackgroundTaskIdentifier  = .invalid

此属性标识要在后台运行的任务请求。

接下来将以下方法添加到CompleteTaskModel之前resetCalcuation()

func  registerBackgroundTask () {
  backgroundTask =  UIApplication .shared.beginBackgroundTask { [ weak  self ] in 
    print ( "iOS has signaled time has expired" )
     self ?.endBackgroundTaskIfActive()
  }
}

registerBackgroundTask()告诉 iOS,当应用程序移至后台时,您需要更多时间来完成您正在做的任何事情。返回的值是此任务的标识符,以便您可以在完成时告诉 iOS。在此调用之后,如果您的应用程序移动到后台,它仍然会获得 CPU 时间,直到您调用endBackgroundTask(_:).

好吧,至少有一些 CPU 时间。

结束完成任务

endBackgroundTask(_:)如果你在后台一段时间后不调用,iOS 会调用你调用时定义的闭包beginBackgroundTask(expirationHandler:)。这使您有机会停止执行代码。

所以最好打电话endBackgroundTask(_:)告诉系统你已经完成了。如果您在此块运行后不调用它并继续执行代码,iOS 将终止您的应用程序!

在下面添加此方法registerBackgroundTask()

func  endBackgroundTaskIfActive () {
   let isBackgroundTaskActive = backgroundTask != .invalid
   if isBackgroundTaskActive {
     print ( "后台任务结束。" )
     UIApplication .shared.endBackgroundTask(backgroundTask)
    背景任务= .invalid
  }
}

如果它被主动注册并将其 ID 重置为invalid ,这将结束后台任务。

注册和结束后台任务

现在,对于重要的部分:更新onChangeOfScenePhase(_:)以注册和结束后台任务,具体取决于应用程序是移动到后台还是活动状态。

将这两个 case 语句替换为以下内容:

case .background:
  让isTimerRunning = updateTimer !=  nil
  让isTaskUnregistered = backgroundTask == .invalid

  if isTimerRunning && isTaskUnregistered {
    注册背景任务()
  }
案子.活动:
  endBackgroundTaskIfActive()

当更改为后台状态时,这会在任务运行但未注册时注册任务。当变为活动状态时,它将结束后台任务。

beginPauseTask()中,在后面添加这一行updateTimer = nil

endBackgroundTaskIfActive()

现在,当用户停止计算时,您调用endBackgroundTask(_:)以向 iOS 表明您不需要任何额外的 CPU 时间。

注意: endBackgroundTask(_:)每次打电话时都打电话很重要beginBackgroundTask(expirationHandler:)。如果您调用beginBackgroundTask(expirationHandler:)两次并且只调用endBackgroundTask(_:)其中一个任务,您仍然会获得 CPU 时间,直到您endBackgroundTask(_:)使用第二个后台任务的标识符再次调用。

构建并运行,然后切换到第三个选项卡。

开始计算斐波那契数列

点击播放并观看应用程序计算那些甜蜜的斐波那契值。将应用程序发送到后台,但在 Xcode 的控制台中观察输出。您的应用程序应在剩余时间减少时继续更新数字。

在大多数情况下,这个时间会从 30 秒开始下降到 5 秒。如果您在达到 5 秒(或您看到的任何值)时等待时间到期,iOS 会调用到期块。

您的应用应该很快就会停止生成输出。然后,如果您返回应用程序,计时器应该再次开始触发,斐波那契疯狂将继续。

在活动和后台之间切换,看看您如何通过每次切换获得额外的时间块。

关于本后台模式教程的最后一个主题:后台获取。

后台获取

iOS 7 中引入了后台获取。它可以让您的应用程序保持最新状态,同时最大限度地减少对电池寿命的影响。从 iOS 13 开始,Apple 引入了一个新的后台任务调度程序 API,它提供了显着的改进。

例如,假设您正在应用程序中实现新闻提要。在后台获取之前,您将在每次应用启动时刷新提要。

可悲的是,当刷新发生时,用户会在几秒钟内看到旧标题。你知道有些人会试图点击一个故事,结果却发现它消失了,取而代之的是一个不相关的故事。不好看。

如果用户打开您的应用程序时神奇地出现了最新的头条新闻,那不是更好吗?这是后台获取给你的能力。

启用后,系统会利用使用模式来确定何时触发后台提取。例如,如果用户在大多数情况下在上午 9 点打开您的新闻应用程序,则可能会在上午 9 点之前的某个时间发生后台抓取。系统决定发出后台抓取的最佳时间,因此,它不适合关键更新。

了解后台获取

后台获取由BGTaskScheduler控制,这是一个复杂的系统,用于平衡影响用户体验的所有因素,例如性能、使用模式、电池寿命等。

后台获取通常涉及从外部源(如网络服务)获取信息。出于本背景模式教程的目的,您将获取当前时间并且不会使用网络。

为了实现后台获取,您需要执行这些任务——但不要执行它们:

  • 选中应用CapabilitiesBackground Modes中的 Background fetch框。****
  • Info.plist为您的刷新任务添加一个标识符。
  • 在您的应用程序委托中调用BGTaskScheduler.register(forTaskWithIdentifier:using:launchHandler:)以处理后台获取。
  • 创建一个BGAppRefreshTaskRequest用于earliestBeginDate何时执行的。
  • 使用 提交请求BGTaskScheduler.submit(_:)

与后台完成任务类似,您有一个短暂但不确定的时间框架来执行后台获取。共识数字是最多 30 秒,但计划更少。如果您需要下载大型资源作为提取的一部分,请使用URLSession的后台传输服务。

实现后台获取

是时候开始了。首先,简单的部分:检查Signing & Capabilities下的 Background fetch mode 功能。**

检查后台获取模式

接下来,打开Info.plist并点击 + 以添加新标识符。

向下滚动并选择Permitted background task scheduler identifiers。展开该项目,然后点击该新标识符旁边的 +以添加一个条目。

键入com.mycompany.myapp.task.refresh作为标识符的值。

注意:在您的实际项目中,您将反向使用您公司的 URL 作为标识符的根,添加您的应用程序名称和描述性元素,例如task.refresh。您可以定义多种刷新任务,每种都有自己的标识符。

添加允许的后台任务标识符

接下来,您需要一个AppDelegate类,因为 iOS 期望您在application(_:didFinishLaunchingWithOptions:).

App文件夹中,添加一个名为AppDelegate.swift的新 Swift 文件。然后用以下代码替换现有代码:

导入UIKit
导入BackgroundTasksAppDelegate : UIResponder , UIApplicationDelegate {
   static  var dateFormatter: DateFormatter  = {
     let formatter =  DateFormatter ()
    格式化程序.dateStyle = .short
    formatter.timeStyle = .long
    返回格式化程序
  }()

  变量窗口:UIWindow?

  func 应用程序(
     _ 应用程序:UIApplication,
     didFinishLaunchingWithOptions  launchOptions:[ UIApplication.LaunchOptionsKey:Any ] ?= nil- 
  > Bool {
     return true   
  }
}

此代码为刷新时间戳定义了一个日期格式化程序。它还包括一个空方法application(_:didFinishLaunchingWithOptions:),用于注册后台获取任务。

现在将以下函数添加到AppDelegate

func  refresh () {
   // 模拟一次刷新,只需将上次刷新日期
  // 更新为当前日期/时间
  let formattedDate =  Self .dateFormatter.string(from: Date ())
   UserDefaults (
    格式化日期,
    forKey: UserDefaultsKeys .lastRefreshDateKey)
   print ( "刷新发生" )
}

这个函数是模拟一次刷新。

在您创建的应用程序中,您可能会从网络中获取数据。对于本教程,您将保存格式化的时间戳以UserDefaults显示刷新执行的时间。

仍然在AppDelegate.swift中,将以下函数添加到AppDelegate

func  scheduleAppRefresh () {
  让请求=  BGAppRefreshTaskRequest (
    标识符:AppConstants .backgroundTaskIdentifier)
  request.earliestBeginDate =  Date (timeIntervalSinceNow: 1  *  60 )
   do {
     try  BGTaskScheduler .shared.submit(request)
     print ( "后台刷新计划" )
  } catch {
     print ( "无法安排应用刷新(error.localizedDescription) " )
  }
}

在这里,您创建一个然后从当前时间BGAppRefreshTaskRequest开始分配一分钟。earliestBeginDate然后您使用 提交请求BGTaskScheduler.submit(_:)

现在,将主体替换为application(_:didFinishLaunchingWithOptions:)

BGTaskScheduler .shared.register(
  forTaskWithIdentifier: AppConstants .backgroundTaskIdentifier,
  using: nil ) { task in 
    self .refresh() // 1 
    task.setTaskCompleted(success: true ) // 2 
    self .scheduleAppRefresh() // 3
}

scheduleAppRefresh()
返回 真

当 iOS 完成启动应用程序时,此代码会将任务注册到任务调度程序并安排第一次刷新。任务本身在执行时将:

  1. 执行刷新。
  2. 将任务标记为成功完成。
  3. 安排下一次刷新。

现在您需要将您的连接AppDelegateAppMain. 打开AppMain.swiftbody在:之前添加这一行

@UIApplicationDelegateAdaptor ( AppDelegate.self ) var appDelegate _ _

这就是 iOS 调用AppDelegate.

在物理设备上构建和运行应用程序。检查 Xcode 控制台中的消息以确认已安排后台刷新。

测试后台获取

测试后台获取的一种方法是坐下来等待系统决定执行此操作。但是您可能会坐很长时间等待这种情况发生。

iOS 不保证您的刷新何时执行。系统使用多种因素来决定何时执行,例如应用程序使用模式、电池电量等。幸运的是,Xcode 为您提供了一种使用调试器命令触发后台获取的方法。

打开RefreshView.swift并在print("moved to background").

然后将应用程序发送到后台,Xcode 应该会在您的新断点处中断。在lldb提示符下,键入(或者,因为它相当复杂,所以复制并粘贴!)以下命令:**

e - l objc -- (void)[[ BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@ "com.mycompany.myapp.task.refresh" ]

这将指示调试器立即执行后台刷新。

恢复应用程序的执行。控制台应显示发生刷新,然后后台刷新计划。每次刷新都会为将来安排另一个刷新。您还可能会看到来自后台任务调度程序的调试消息,指示其活动。

接下来,重新打开应用程序。刷新选项卡将显示刷新发生的时间和日期。

如果您将应用程序留在设备上并在接下来的几天内查看,您会看到时间戳不时更新。iOS 根据其计算的最佳刷新时间来调用刷新。

结论

对于需要几分钟才能完成的长时间运行的后台任务,请了解有关后台处理任务的更多信息。后台处理任务类似于后台提取,但用于更严格的任务,例如数据处理和维护。

还有两个与后台模式相关的优秀 WWDC 演示:

  1. 应用程序后台执行的进步:讨论最近对后台处理选项的改进。
  2. 后台执行揭秘:将帮助您更深入地了解不同的后台模式。

最后,您可以在配置后台执行模式中了解所有后台执行模式。

点击下载项目资料与查看原文

这里也推荐一些面试相关的内容!