应用程序在后台运行时,位置跟踪会在一段时间后停止。

33
我创建了一个简单的应用程序,它可以跟踪用户位置并在每次位置更新时创建本地通知。 我开启了以下后台模式: enter image description here
let locationManager = CLLocationManager()

open override func viewDidLoad() {
       locationManager.delegate = self;
       locationManager.desiredAccuracy = kCLLocationAccuracyBest;
       locationManager.distanceFilter = 10
       locationManager.allowsBackgroundLocationUpdates = true
       locationManager.startUpdatingLocation()
}

open func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
       let notification = UILocalNotification()
       notification.alertBody = "location updated"
       notification.fireDate = Date()
       UIApplication.shared.scheduleLocalNotification(notification)
}

我设置了NSLocationAlwaysUsageDescription字符串并请求权限。当应用程序第一次加载时,用户授予始终使用的权限。


当应用程序在前台时,它运行良好,在后台时仍然有效,至少在5-40分钟的时间范围内,这取决于电池或其他已打开的应用程序而有所改变。

问题是为什么它停止工作,难道不应该保持工作状态吗?

我从未在Apple文档中看到过时间限制。


而且对于授权,你请求了 requestAlwaysAuthorization(),对吧? - Ahmad F
@AhmadF,是的,“即使您没有使用该应用程序,允许'app'访问您的位置?”已经提示。我已经允许,在iPhone隐私设置中,该应用程序的位置服务显示“始终”,毫无疑问。 - Okan Kocyigit
@ocanal:只需参考此网址 https://dev59.com/8mw15IYBdhLWcg3w3vle - Bhadresh Sonani
@ocanal,你可能需要检查一下这个答案,确保你为你想要实现的目标设置了适当的键;我已经更新了我的答案,希望能有所帮助 :) - Ahmad F
6个回答

37

当应用程序切换到后台时,请切换至“显着位置更新”。如果应用程序在后台无限期保持活动状态,iOS将卸载该应用程序。


locationManager.pausesLocationUpdatesAutomatically = false

1
根据我的测试结果,这对我的项目有效。但我必须更正术语“重要位置”,这不是“重要位置”,那是另一回事,Anand Suthar已经对此发表了一些看法。 - Okan Kocyigit
5
@ocanal,仅供参考,如果没有它,它会在大约17分钟后停止(其他人之前在他们的问题中提到过。我已经验证了)。你所说的5-40是移动的结果,如果你不动,大约是17分钟左右。如果将"pausesLocationUpdatesAutomatically"设置为false,则可能会耗尽电池。请注意,只有当应用程序在前台时暂停位置更新时,它才能恢复,否则您无法在后台进行任何操作以恢复它。文档非常简陋。 - mfaani
3
建议设置一个计时器,例如在移动100米后的5分钟内,将精度降低至1公里/3公里。在降低精度之前,请保存最后位置...然后使用另一个计时器:每5/10分钟短暂地提高精度到您想要的水平,并将该位置与您保存的位置进行比较。如果您没有移动太远(例如100米),则保持精度降级,否则提高精度。(时间和米数根据您的业务需求而变化。您可能希望不要长时间降低精度)。 - mfaani
@ocanal 目前,应用程序的电池使用情况不是主要关注点。目标是在接收每个更新后获取位置更新并运行计时器以执行某些任务。我怀疑当应用程序长时间在后台运行时是否会停止获取位置更新。所以对你来说它工作得很好吗?这是我第一次使用位置服务。我尝试过其他方法,如访问监视服务,但遇到了一些问题(https://stackoverflow.com/questions/51012580/properly-detect-all-visits-using-visit-monitoring-service) - Sujal
1
@Sujal,是的,我已经测试了5个小时,应用程序没有卸载,并且我一直进行位置跟踪5个小时而没有任何问题。但是iOS方面的事情在不断变化,所以您应该控制更新版本,如iOS 12,根据我的测试结果,它适用于iOS 11。 - Okan Kocyigit
显示剩余4条评论

14

降低精度

将位置管理器对象的desiredAccuracy属性设置为更低的值。

self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation

您可以使用其中一个CLLocationAccuracy常量。

重要提示

iOS 设备上默认情况下,标准位置更新以最佳精度级别运行。请更改这些设置以匹配您的应用程序要求。否则,您的应用程序会浪费不必要的能源。


自动暂停

将位置管理器对象的pausesLocationUpdatesAutomatically属性设置为true

self.locationManager.pausesLocationUpdatesAutomatically = true

重要提示

对于使用授权的应用,暂停位置更新会导致在启动应用并能够重新开始这些更新之前无法访问位置更改。如果不希望位置更新完全停止,请考虑禁用此属性并将位置精度更改为kCLLocationAccuracyThreeKilometers,当您的应用程序进入后台时。这样可以以节能的方式继续接收位置更新。


允许后台更新

将位置管理器对象的allowsBackgroundLocationUpdates属性设置为true

self.locationManager.allowsBackgroundLocationUpdates = true

如果应用程序在挂起状态下想要接收位置更新,则必须在其应用程序的Info.plist文件中包含UIBackgroundModes密钥(值为location),并将此属性的值设置为true。必须存在具有位置值的UIBackgroundModes密钥以进行后台更新。


指定活动类型

设置activityType属性,让Core Location知道您的应用程序在给定时间执行的位置活动类型。

self.locationManager.activityType = .automotiveNavigation 
你可以使用其中一个 CLActivityType 案例。

延迟位置更新

在支持GPS硬件的设备上,当你的应用程序在后台运行时,你可以让位置管理器推迟位置更新的传递。例如,跟踪用户在远足小径上的位置的健身应用程序可以推迟更新,直到用户移动了一定的距离或经过了一定的时间。



感谢详细的解释。虽然我没有提到我的应用程序,但正如我在Anand Suthar的答案中所评论的那样,这类似于导航应用程序,因此我无法降低精度,如果我将pausesLocationUpdatesAutomatically设置为true,它将会在一段时间后停止,这就是我问这个问题的原因。因此,您的答案适用于想要创建节能型应用程序的人。再次感谢您提供详细的解释。 - Okan Kocyigit
@ocanal 我看了你的评论。我有一个问题。你尝试过推迟位置更新吗? - Adrian Bobrowski
抱歉,我参考了另一条评论,这里是我解释的评论。 - Okan Kocyigit
你说的"deferring"是什么意思? - Okan Kocyigit
@ocanal 在我看来,iOS 允许应用程序获取您的位置信息是因为这会消耗电池。 - Adrian Bobrowski

3
在寻找参考文献(谈论任何限制)之后,我认为苹果核心位置最佳实践视频会话可能有用!在06:53谈论后台中的标准位置:
此外,核心位置不会采取任何措施来确保您的应用程序继续运行,因此,如果您由于某种原因需要在后台运行并且决定启动位置会话,则可能会收到一些更新,但是在接收到所有希望收到的信息之前,也可能会被暂停...
实际上,我以前遇到过这个问题,-作为解决方法-使用核心位置来跟踪用户的位置以执行与其位置无关的功能 - 即上传文件 - 但是自从iOS 9发布以来,这种解决方法就不再有效了;我甚至发布了一个问题引用此问题。
然而,如果您的情况与我面对的情况不太相同,如果您的目标是:
创建本地通知以每次更新位置。
那么你可能需要遵循与用户通知框架集成的方法 - UNLocationNotificationTrigger
用户必须到达的地理位置才能启用本地通知。
这也在视频会话中提到(08:59)。
可能,这可能不是您正在寻找的内容,但由于我们无法保证后台执行将继续运行,因此您可能会-以某种方式-找到一种将其集成到应用程序中以实现所需功能的方法。
iOS 11更新:
您可能需要检查此答案以请求位置访问的适当方式。

感谢提供的视频参考和搜索。实际上,本地通知仅用于调试,并不是我正在处理的内容。我正在开发一款出租车司机应用程序,该应用程序需要在后台获取司机位置并将其发送到服务器。我已经创建了一个套接字连接并通过它向服务器发送数据。因此,当应用程序进入后台时,我们似乎无法保证保持套接字连接活动。是否有任何事件会在应用程序终止我的套接字连接和位置更新或可能清除我的实例时被调用? - Okan Kocyigit
其实,我不确定那个... 你可能需要检查一下什么是适合你情况的后台执行方式,希望你能找到解决方案。 - Ahmad F
@ocanal,你是否找到了一种能够保持套接字连接的解决方案? - illis69
@illis69 是的,已采纳的答案对我有效,我已经测试并发布了项目。它一直运行良好。 - Okan Kocyigit

1
根据声音,该应用程序由于内存限制而被终止。然而,当新位置可用时,它应该重新启动,如此处所述:https://developer.apple.com/documentation/uikit/uiapplicationlaunchoptionskey/1623101-location 您应该看到调用了 application(_:didFinishLaunchingWithOptions:),并且 'location' 键应该存在于启动选项中。然后,您可以重新创建消耗位置的任何内容,并继续记录。
如果它没有重新启动,可能是因为它需要太多内存。检查应用程序的内存使用情况,并查看是否调用了 applicationDidReceiveMemoryWarning(_:)

我正在进行调试。我为事件设置了断点,应用程序在24分钟后停止了位置更新,但applicationDidReceiveMemoryWarning没有被调用,应用程序仍在工作和调试仍在进行中。我正在等待application(_:didFinishLaunchingWithOptions:)看它是否再次被调用。 - Okan Kocyigit
哦,实际上应用程序不会重新启动,因为它仍在工作 :) - Okan Kocyigit

1
我遇到了这样一个问题,QT应用在一两个小时后会停止接收后台位置事件。
在App Delegate中,当应用进入后台时,我会停止CLLocationManager,并将精度从kCLLocationAccuracyBest降低到kCLLocationAccuracyNearestTenMeters,并将距离过滤器从无更改为50米,然后它可以持续跟踪24小时以上。

1
我假设您还没有实现后台任务。您可以在这里阅读相关内容。
在上述链接中,第“实施长时间运行的任务”部分,第3点是您的情况,因此您可以在项目中使用后台位置更新,并且为此需要实现后台任务。
有三种方法可以跟踪用户位置(根据上述链接中的“跟踪用户位置”部分):-
  1. 仅前台位置服务(适用于您的情况)

  2. 显著位置变化服务(推荐使用),但我认为在您的情况下无法使用,因为您想每10米更新一次用户位置,而它只能在约500米范围内工作,更多信息请参见这里

  3. 后台位置服务(我认为您正在尝试此项服务),解决方案是添加一个后台任务。

    以下是后台任务的示例,您可以根据自己的需求进行修改,它已经在我的应用中工作了2个小时,仍然可以在后台更新位置。

请在您的AppDelegate类中更新以下函数,然后在后台运行您的应用程序。

func applicationDidEnterBackground(_ application: UIApplication) {
    application.beginBackgroundTask(withName: "") {}
}

以下是我的ViewController类。
import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

    let locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()
    
        locationManager.delegate = self;
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.distanceFilter = kCLDistanceFilterNone
    
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in
            self.locationManager.startUpdatingLocation()
        }
    
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("didUpdateLocations \(Date())")
        self.locationManager.stopUpdatingLocation()
    }

}

那个计时器是干什么用的?为什么我们需要每秒调用 self.locationManager.startUpdatingLocation() - Okan Kocyigit
我正在使用计时器进行测试,它是用于每秒更新用户位置的。您可以使用自己的代码替代我的。主要的事情是后台任务 -> 您需要将其添加到AppDelegate类中。 - Anand Suthar
@ocanal,“后台任务”对你没用吗?但是我能够在后台无限时间运行应用程序,并且跟踪用户位置而没有任何问题。 - Anand Suthar
1
我也会尝试这个,但是我需要至少测试4-5个小时才能确定它是否有效。但在苹果文档中提到:“运行后台任务的应用程序有一定的时间来运行它们。(您可以使用backgroundTimeRemaining属性查看可用的时间)。如果在时间到期之前不为每个任务调用endBackgroundTask(_ :),系统将终止应用程序。” - Okan Kocyigit
是的,你需要实现一个有限时间任务,你可以在我回答中发布的第一个链接中阅读到这个内容,因为你可以使用“[application endBackgroundTask:bgTask]”和“bgTask = UIBackgroundTaskInvalid”。 - Anand Suthar

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