首页 > 解决方案 > 后台定位服务在一段时间内停止工作

问题描述

我正在开发一个 iOS 应用程序,用户可以将他们的旅行路线保存到服务器(通过 API 发布他们的位置)。我正在努力解决的问题是有几个用户报告说他们保存的路线在旅途中被打断了。具体来说,当用户在地图上查看他们保存的路线时,有一部分路线只是一条直线,因为该路线部分的位置没有发送到服务器,或者只是因为当时设备无法接收位置。奇怪的是,其余的路线记录正常,所以位置服务似乎停止工作了一段时间,但之后又重新开始,所以我的应用程序可以很好地记录它。

而最令人沮丧的是,我无法重现这个问题。

以下是用户报告的问题的情况:
- 用户启动应用程序然后锁定设备屏幕并将其放在口袋里,他们在整个旅程中没有触摸它。没有发生电池耗尽或崩溃。
- 在行驶了大约 8-9 公里后一切正常,路线记录在接下来的 ~ 65 公里中被中断,然后在剩下的 ~ 80 公里中再次记录良好。

以下是我的项目设置:
- 功能中的背景模式处于打开状态,并带有位置更新。
- 从位置服务接收到的位置会根据时间戳和准确性进行过滤,如果位置成功发送到服务器,则使用“isSent”标志标记保存到核心数据中。这样我的应用程序可以覆盖网络连接中断的情况。
- 标有错误“isSent”标志的位置将每 30 秒发送到服务器。

我的 LocationManager 代码:

class LocationManager: NSObject, CLLocationManagerDelegate {

    var locationManager: CLLocationManager = {
        var _locationManager = CLLocationManager()
        _locationManager.delegate = self
        _locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
        _locationManager.activityType = .automotiveNavigation
        _locationManager.allowsBackgroundLocationUpdates = true
        _locationManager.pausesLocationUpdatesAutomatically = false

        return _locationManager
    }()


    func startLocationService() {
        locationManager.startUpdatingLocation()
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.pausesLocationUpdatesAutomatically = false
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        var positionsToSavedToDb: [DbPositionModel] = []

        for location in locations {
            let howRecent = location.timestamp.timeIntervalSinceNow

            guard abs(location.timestamp.timeIntervalSinceNow) < 10 && location.horizontalAccuracy > 0 && location.horizontalAccuracy < 33 else {
                continue
            }

            if self.locations.count > 0 {
                let distanceSinceLastLocation = location.distance(from: self.locations.last!)
                let timeSinceLastLocation = location.timestamp.timeIntervalSince(self.locations.last!.timestamp)
                if (distanceSinceLastLocation < 5 && abs(timeSinceLastLocation) < 10) {
                    continue
                }
            }

            // Create DbPositionModel from location and append to positionsToSavedToDb
        }

        // Save positionsToSavedToDb to core data
    }

    @objc func each30Seconds(_ timer: Timer) {
        // Select in database DbPositionModel objects with false “isSent” flag to send to server
    }
}

你们能帮我找出我的代码中的黑洞或我能做些什么来重现/解决这个问题吗?非常感谢!!!

标签: ioscore-locationios-background-mode

解决方案


你的设置对我来说很好。就一个问题。当你说它在 60 公里内不起作用,然后它开始在 80 公里内工作,那是在后台运行吗?我的意思是用户不需要进入前台就可以重新开始工作,对吗?

您的限制location.horizontalAccuracy是 33。您认为它会非常准确。我不确定,也许设备/城市组合不好,然后你会提前回来。我建议你记录你提前退出的原因。他们的城市可能与你的不同。此外,我听说 iPhoneX 的 GPS 即使位置正确,它的horizontalAccuracy. 这主要发生在 iPhoneX 用户身上吗?

enum LocationAccuracyError: Error {
    case stale(secondsOld: Double)
    case invalid
    case lowAccuracy(metersOff: Double)
}

extension LocationAccuracyError: LocalizedError{
    var errorDescription: String? {
        switch self {
        case .stale(let seconds):
            return NSLocalizedString("location was stale by: \(seconds) seconds", comment: "")
        case .invalid:
            return NSLocalizedString("location was invalid)", comment: "")
        case .lowAccuracy(let metersOff):
            return NSLocalizedString("location's horizontal Accuracy was off by likely more than: \(metersOff) meters" , comment: "")
        }
    }
}

然后有这样的功能来检查每个位置。

private func checkLocationAccuracy(from location: CLLocation) throws {

    let ageOfLocation = -location.timestamp.timeIntervalSinceNow

    if ageOfLocation >= maximumAcceptedStale {
        throw LocationAccuracyError.stale(secondsOld: ageOfLocation)
    }

    if location.horizontalAccuracy <= 0 {
        throw LocationAccuracyError.invalid
    }

    if location.horizontalAccuracy > MaximumAcceptedHorizontalAccuracy{
        throw LocationAccuracyError.lowAccuracy(metersOff: location.horizontalAccuracy)
    }
}

您的最终用途如下:

do {
       try checkLocationAccuracy(from: location)

   } catch let error {
       writelog("Bad Location: \(error.localizedDescription)")
   }

我还会在您的应用程序状态周围添加日志,例如添加日志以捕获didEnterBackground


推荐阅读