挂起/终止时重要位置API的行为?

110
这是CLLocationManager文档中描述使用startMonitoringSignificantLocationChanges时应用程序行为的部分内容:
如果您启动此服务并随后终止应用程序,则系统会自动将应用程序重新启动到后台,以便在出现新事件时使用。在这种情况下,传递给应用程序委托的application:didFinishLaunchingWithOptions:方法的选项字典包含键UIApplicationLaunchOptionsLocationKey,以指示您的应用程序是由于位置事件而启动的。重新启动后,您仍然必须配置一个位置管理器对象并调用此方法以继续接收位置事件。当您重新启动位置服务时,当前事件会立即传递给您的委托。此外,在您启动位置服务之前,位置管理器对象的位置属性已填充了最新的位置对象。
所以我的理解是,如果您的应用程序终止(我假设如果您不从applicationWillTerminate调用stopMonitoringSignificantLocationChanges),则会在application:didFinishLaunchingWithOptions中使用UIApplicationLaunchOptionsLocationKey参数唤醒您。此时,您创建CLLocationManager,调用startMonitoringSignificantLocationChanges并进行limited time的后台位置处理。所以我对这一部分很满意。
上一段只是讨论了应用程序终止时会发生什么,并没有建议在应用程序挂起时应该做什么。didFinishLaunchingWithOptions 的文档说:
“应用程序在后台跟踪位置更新,被清除,现在已经重新启动。在这种情况下,字典包含一个键,指示应用程序由于新的位置事件而重新启动。”
这表明,只有在您被终止后,由于位置更改而启动应用程序时,您才会收到此调用。
然而,在 Location Awareness Programming Guide 中,Significant Change Service 的段落则有如下说法:
如果您保持此服务运行并且应用程序随后被暂停或终止,则服务将在新的位置数据到达时自动唤醒您的应用程序。在唤醒时,您的应用程序被放置在后台,并被赋予一小段时间来处理位置数据。由于您的应用程序在后台运行,因此它应该尽量少做工作,并避免任何可能阻止其在分配的时间到期之前返回的任务(例如查询网络)。否则,您的应用程序可能会被终止。
这表明如果您的应用程序已被暂停,您将通过位置数据被唤醒,但未说明如何唤醒您:

在撰写本文的过程中,我想我可能已经回答了自己的问题,但是由更有经验的人确认我的理解将是很好的。


你能解释一下“freeze dried”是什么意思吗? - mfaani
@honey,我为那个平庸的比喻道歉。当时我的意思是,位置管理器的状态在应用程序暂停时保存。不确定现在是否仍然如此。已经有一段时间了:) - RedBlueThing
4个回答

82
自从我问了这个问题以来,我进行了大量测试(主要是在家和工作之间的火车上),并确认了挂起应用程序的行为与我在问题末尾所怀疑的一样。
也就是说,您的挂起应用程序被唤醒,您不会收到任何有关应用程序委托的回调,而是通过现有的CLLocationManagerDelegate接收位置更新。您可以通过检查applicationState来检测您是否在后台运行,并针对从挂起状态唤醒以执行位置处理的情况进行有限工作。
[UIApplication sharedApplication].applicationState == UIApplicationStateBackground

我使用一个位置测试工具得出了这个结论,你可以下载并尝试。这是一个很简单的应用程序,允许你通过用户界面打开重大更改和GPS更改API,并记录所有返回的响应。

注意:上一个答案中的第六点不正确。冻干的挂起应用程序在从挂起状态唤醒时确实会收到CLLocationManagerDelegate回调。


1
我在我的回答中划掉了第6点,以免让人们感到困惑。 - Aaron
具有讽刺意味的是,在开发这个重大变化时,我也感到了同样的困惑。我仍然对如果应用程序关闭会发生什么持有一些疑问。UIApplication委托回调不会发生吗?那是否是启动应用程序的正确方式?如果位置管理器还没有启动,它如何在后台接收通知?(是时候进行更多的研究和开发了) - darshansonde
非常感谢提供的示例应用程序,对我非常有用。我有一个问题:如果我在后台使用标准位置服务而不是重大位置变化服务,那么在这种情况下,应用程序是否也会在终止后重新启动?我在这里详细提出了这个问题,如果您能为我解答,我将不胜感激:] https://dev59.com/kmct5IYBdhLWcg3wPbEm - aslı
2
未在iOS7上工作 https://dev59.com/umMk5IYBdhLWcg3w0RCU - Igor
@RedBlueThing有一个问题,我们可以在应用程序被杀死的情况下调用API(更新服务器的经纬度)吗? - Bhavesh Dhaduk
@BhaveshDhaduk 很久没用过这些API了,但是当应用从冷启动时,你肯定可以创建网络请求。 - RedBlueThing

26

我理解如下(我正在编写一个依赖此API的应用程序,但还未完成该组件以开始测试):

  1. 第一次运行您的应用程序时,您注册了 startMonitoringSignificantLocationChanges 并提供回调函数。当您的应用程序运行时,每当它接收到重大更改时,它都会调用该回调。
  2. 如果您的应用程序被放到后台,UIApplication 将接收 applicationWillResignActive,然后是 applicationDidEnterBackground
  3. 如果在挂起状态下终止您的应用程序,则不会通知您;但是,如果在运行中终止您的应用程序(我了解到无论是前台还是后台),您将获得一个瞬间来处理 applicationWillTerminate。您无法从此函数请求额外的后台时间。
  4. 尽管在后台被终止,操作系统仍会重新启动您的应用程序。如果您的应用程序仅由操作系统启动进行更改,您将收到对 application didFinishLaunchingWithOptions 的调用:
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey])

这将帮助您确定是否从后台位置更改返回。

  1. 相反,如果您当前在后台运行,并且用户手动重新启动应用程序,则会收到applicationWillEnterForeground,然后是applicationDidBecomeActive

  2. 不管它是怎样发生的,当您的应用程序重新启动时(除非它仍作为后台任务而在该任务已开始监视更改的情况下继续运行),您需要显式地告诉它再次启动startMonitoringSignificantLocationChanges,因为在“冷凍乾燥”之后回调不再连接。一旦从暂停状态回来并重新附加了某种位置处理程序,您只需要在didUpdateToLocation中实现代码即可。

这就是我目前正在进行的代码开发。正如我之前提到的,我还没有准备好在设备上进行测试,所以我无法确定我是否正确解释了一切,因此评论者,请随时纠正我(尽管我已对该主题进行了大量阅读)。

哦,如果不幸的是,您发布了一个能够实现我想要做的事情的应用程序,我可能会哭 :)

祝你好运!


1
@Tegeril +1 感谢您的回答。我开始听到这个问题的沉默了。 :) 我很惊讶应用程序从挂起状态开始需要重新调用startMonitoringSignificantLocationChanges。您是否有描述此内容的文档链接?我的理解是,从挂起状态加载将实例化所有对象,就像应用程序挂起时一样。因此,我希望重大更改请求能够生效。 - RedBlueThing
Morgan Grainger在Dev论坛上建议说:“您需要创建一个CLLocationManager,设置代理,并在应用程序启动时调用startMonitoringSignificantLocationChanges,否则Core Location将无处传递更新。” ,这是在重新启动应用程序时的情况,无论从终止还是挂起状态。来自“在startMonitoringSignificantLocationChanges之后重新启动应用程序,现在该怎么办?” - Aaron
我同意即使在那个论坛帖子的背景下,这仍然可能是模糊的(发帖人正在讨论从后台唤醒,但摩根使用了更一般的应用程序重新启动)。我仍然认为,如果您想使用重大更改功能,则需要在应用程序重新启动时再次调用监视器。如果您想启动后台任务并使用标准位置服务和GPS功能进行更新,则这是另一种选择。我不认为您需要在每次使用重大更改时重新注册才能再次启动。 - Aaron
@Tegeril 嘿 :) 希望如此。也许是时候乘火车测试一下了。在四处走动时调试很棘手。 - RedBlueThing
1
我很高兴这件事情有了明确的结果,这应该会在未来对我有所帮助 :) - Aaron
显示剩余5条评论

1
如果应用程序由于位置更改而从挂起状态调用,则应用程序将在后台状态下启动。
所有对象都将保持活动状态,您将在现有委托中收到位置更新。

0

我有一个关于应用程序终止时的重要提示:

对于一些可以在后台启动应用程序并稍后需要接收处理该启动的回调的API,可能需要设置delegate对象。

因此,如果您由于位置跟踪而获得了一个应用程序启动(即从终止状态而非挂起状态),则除非您在didFinishLaunching中设置委托,否则不会调用您的locationManager委托回调。更准确地说,在设置委托之前,您将不会收到任何委托回调。

  1. 因此,如果您在应用程序启动后在ABC视图控制器中进行后续操作
let manager = CLLocationManager()
manager.delegate = self
  1. 然后应用程序被终止了
  2. 然后应用程序由于后台位置更改而被定位
  3. 除非将应用程序前台化并打开ABC视图控制器,否则您将不会收到回调

错误的想法是认为应用程序神奇地知道委托对象是什么。它不会持久保存该信息。

解决方案:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
   let myLocationManager = CLLocationManager()
   myLocationManager.delegate = self
}

我在处理一个完全不相关的API时弄清了这个问题:

optional func userNotificationCenter(_ center: UNUserNotificationCenter, 
                      didReceive response: UNNotificationResponse, 
              withCompletionHandler completionHandler: @escaping () -> Void)

这意味着,如果您在 AppLaunch 之前没有设置委托,则会错过应用程序终止后立即发生的通知与用户交互。更准确地说,直到您设置 userNotification 的委托之前,您将错过任何回调。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接