【问题标题】:Location tracking stops after a while when app is in the background当应用程序在后台运行一段时间后位置跟踪停止
【发布时间】:2018-01-20 03:10:10
【问题描述】:

我创建了一个简单的应用程序,它可以跟踪用户位置并在每次更新位置时创建本地通知。

我启用了下面的背景模式,

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(),对吧?
  • @AhmadF,是的“即使您不使用应用程序,也允许“应用程序”访问您的位置?”被提示。我已经允许了,毫无疑问,在 iPhone 隐私设置中,该应用程序的位置服务显示为“始终”。
  • @ocanal : 请参考这个网址stackoverflow.com/questions/6347503/…
  • @ocanal 您可能需要检查this answer 以确保您为您的目标设置了适当的键;我已经更新了我的答案,希望对您有所帮助:)

标签: ios swift core-location cllocationmanager


【解决方案1】:

我遇到了这个问题,QT 应用在一两个小时后会停止在后台获取位置事件。

在 App Delegate 中,当应用进入后台时,我会停止 CLLocationManager,将准确度从 kCLLocationAccuracyBest 降低到 kCLLocationAccuracyNearestTenMeters,并将距离过滤器从 None 增加到 50 米,然后跟踪 24 小时以上。

【讨论】:

    【解决方案2】:

    我假设您还没有实现后台任务。你可以阅读here

    在上述“实施长时间运行的任务”部分下的链接中 点没有。 3是你的情况,所以你可以在你的项目中使用后台位置更新是有效的,同样你也需要实现一个后台任务。

    有三种方法可以跟踪用户位置(根据上面“跟踪用户位置”部分下的链接):-

    1. 仅限前台的定位服务(适用于您的情况)

    2. 显着变化的位置服务(推荐),但我认为它不适用于您的情况,因为您希望每 10 米更新用户位置,并且它适用于约 500 米,更多请看here

    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()
    • 我正在使用计时器进行测试,它用于每秒更新用户位置。您可以改用您的代码。主要是后台任务->您需要在 AppDelegate 类中添加。
    • @ocanal“后台任务”对您不起作用?但我可以在后台无限运行应用程序,并且还可以毫无问题地跟踪用户位置。
    • 我也会尝试这个,但我需要至少测试 4-5 小时,看看它是否有效。但是在apple documentation 上,它说“运行后台任务的应用程序运行它们的时间是有限的。(您可以使用 backgroundTimeRemaining 属性找出可用的时间。)如果您不调用 endBackgroundTask(_:)对于时间到期之前的每个任务,系统都会终止该应用程序。”
    • 是的,您需要实现一个有限时间的任务,您可以在我在回答中发布的第一个链接中阅读此内容,因为您可以使用“[application endBackgroundTask:bgTask]”和“bgTask = UIBackgroundTaskInvalid”。
    【解决方案3】:

    在搜索参考资料(谈论任何限制)后,我认为 Apple Core Location Best Practices 视频会话可能有用! 06:53 在后台谈论标准位置:

    此外,Core Location 不会采取任何措施来确保您的应用 继续运行,所以如果你因为某种原因有后台运行并且 你决定开始一个定位会话你可能会得到一些更新,但是 在您收到所有信息之前,您也可能会被暂停 你希望收到...

    实际上,我之前遇到过这个问题,作为一种解决方法,核心位置用于持续跟踪用户的位置以执行与其位置无关的功能,即上传文件,但是这种解决方法不起作用自 iOS 9 发布以来;我什至发布了一个question 来指代这个问题。

    但是,如果您的目标是:

    ...为每次更新位置创建本地通知。

    那么您可能需要遵循与用户通知框架集成的方法 - UNLocationNotificationTrigger:

    用户必须到达的地理位置才能启用 发送本地通知。

    视频会话中也提到了(08:59)。

    这可能不是您想要的,但由于我们无法保证后台执行将继续运行,您可能会以某种方式将其集成到您的应用中以实现所需的功能。

    iOS 11 更新:

    您可能需要查看this answer 以了解请求位置访问权限的正确方法。

    【讨论】:

    • 感谢视频参考和搜索。实际上本地通知仅用于调试。那不是我正在做的事情。我正在为出租车司机应用程序工作,即使应用程序在后台,它也需要获取司机位置并将其发送到服务器。我创建了一个套接字连接并通过它向服务器发送数据。因此,我们似乎无法保证当应用程序进入后台时我们保持套接字连接。当应用程序终止我的套接字连接和位置更新或清除我的实例时,是否会调用任何事件?
    • 实际上,我不确定...您可能想检查您的案例的适当后台执行是什么,希望您找到适合您案例的解决方案。
    • @ocanal 你有没有找到一个能够让你的套接字连接保持活跃的解决方案??
    • @illis69 是的,接受的答案对我有用,我已经测试并发布了项目。它一直运作良好。
    【解决方案4】:

    降低准确性

    设置位置管理器对象的desiredAccuracy属性

    self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    

    您可以使用CLLocationAccuracy 常量之一

    重要

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


    自动暂停

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

    self.locationManager.pausesLocationUpdatesAutomatically = true
    

    重要

    对于具有使用中授权的应用,暂停位置更新 结束对位置更改的访问,直到再次启动应用程序并且 能够重新启动这些更新。如果您不希望位置更新 完全停止,考虑禁用此属性并更改位置 当您的应用移动时,准确度为 kCLLocationAccuracyThreeKilometers 到背景。这样做可以让您继续接收位置 以省电的方式更新。


    允许后台更新

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

    self.locationManager.allowsBackgroundLocationUpdates = true
    

    希望在暂停时接收位置更新的应用必须包括 他们应用程序中的 UIBackgroundModes 键(带有位置值) Info.plist 文件并将此属性的值设置为 true。这 具有位置值的 UIBackgroundModes 键的存在是 需要后台更新


    指定活动类型

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

    self.locationManager.activityType = .automotiveNavigation 
    

    您可以使用CLActivityType 案例之一


    Defer Location Update

    在支持 GPS 硬件的设备上,您可以让位置 当您的应用程序在 背景。例如,跟踪用户位置的健身应用 在远足径上可以推迟更新,直到用户移动了某个 距离或一定时间已经过去。


    【讨论】:

    • 感谢您的详细解释。我没有提到我的应用程序,但正如我在 Anand Suthar's answer 上评论的那样,这类似于导航应用程序,所以我不能降低准确性,如果我将 pausesLocationUpdatesAutomatically 设置为 true,它会在一段时间后停止,这就是为什么我我问这个问题。因此,您的答案是针对想要创建节能应用程序的人。但再次感谢您提供详细信息。
    • @ocanal 我读过你的 cmets。我有一个问题。您是否尝试推迟位置更新
    • 对不起,我提到了另一条评论,this 是我解释的评论。
    • 延期是什么意思?
    • @ocanal IMO。 iOS 可让您访问您的位置,因为您的应用会消耗电池
    【解决方案5】:

    当应用移至后台时切换到重要的位置更新。如果应用程序无限期地在后台保持活动状态,iOS 将卸载该应用程序。

    locationManager.pausesLocationUpdatesAutomatically = false
    

    【讨论】:

    • 这似乎是一个很好的解决方案,它也在apple developer form 上进行了讨论。我正在测试它,看看发生了什么。
    • 作为我的测试结果,这适用于我的项目。但是我必须更正“重要位置”一词,这不是“重要位置”,这是 Anand Suthar 已经说过的另一件事。
    • @ocanal 仅供参考,如果没有这个,它大约在 17 分钟后停止(其他人之前在他们的问题中提到过这一点。我已经验证过)分钟没有移动。您的 5-40 是移动的结果,如果您保持不动,则大约 17 分钟。如果您将pausesLocationUpdatesAutomatically 设置为false,它可能会耗尽您的电池电量。 仅供参考,只有在应用处于前台时位置暂停时才能恢复,否则您无法在后台恢复它。文档在这方面很差。
    • @ocanal 我建议设置一个计时器,例如在 -100 米运动 5 分钟后,将精度降低到 OneKilometer/ThreeKilometer。在降低您的准确性之前,请保存最后一个位置...然后使用另一个计时器:每 5/10 分钟暂时将准确性升级到您喜欢的任何位置,并将该位置与您保存的位置进行比较。如果您没有移动太多(例如 100 米),则保持降级状态,否则提高精度。 (时间和计量表因您的业务需求而异。您可能希望也可能不希望精度下降太久)
    • @Sujal,是的,我已经测试了 5 个小时,应用程序没有卸载,我保持位置跟踪 5 个小时没有任何问题。但是 ios 方面的情况在不断变化,因此您应该控制 ios 12 等较新版本,因为我的测试结果对于 ios 11 来说很好。
    【解决方案6】:

    听上去,应用程序由于内存限制而被终止。 但是,当有新位置可用时,它应该重新启动,如下所述:https://developer.apple.com/documentation/uikit/uiapplicationlaunchoptionskey/1623101-location

    您应该看到application(_:didFinishLaunchingWithOptions:) 被调用,并且“位置”键应该出现在启动选项中。然后,您可以重新创建消耗位置的任何内容并继续录制。

    如果它没有重新启动,它可能太耗内存了。检查应用程序的内存消耗,看看是否正在调用applicationDidReceiveMemoryWarning(_:)

    【讨论】:

    • 我现在正在调试。我已经为事件设置了断点,应用程序在 24 分钟后停止了位置更新,但是没有调用 applicationDidReceiveMemoryWarning 并且应用程序没有被杀死,仍在工作,调试仍在工作。我正在等待application(_:didFinishLaunchingWithOptions:) 看看它是否再次被调用。
    • hımm,实际上应用程序永远不会重新启动,因为它仍在工作:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 2021-09-05
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    相关资源
    最近更新 更多