【问题标题】:iOS swift backgound location update when killed/suspended with minimum data lossiOS 快速后台位置更新时被杀死/挂起以最小的数据丢失
【发布时间】:2016-12-26 10:59:16
【问题描述】:

关于swift语言的位置更新,我有几个问题要问。

让我解释一下我在应用程序中所做的事情。我正在开发一个应用程序,它会定期监控用户的位置(就像你们所有人一样)并将其更新到服务器,以便跟踪并保存用户的移动以供用户将来参考。

问题

  1. 使用 startMonitoringSignificantLocationChanges 与 startUpdatingLocation 有什么区别

    1.1 如果我们使用 startUpdatingLocation,它是否影响发布应用到 App Store

  2. 当应用被杀死/暂停强制关闭被用户)时,从 AppDelegate 需要 一些时间来重新启动位置管理器这会导致在一段时间内丢失位置数据。有什么可能的解决方案来克服这个问题?

    2.1 重启的时间差在30秒到近1分钟左右,不会触发位置更新,因此路线并不完美如图在图片

应用程序的输出,由于重新启动,位置未收到,因此路线越过道路。

参考代码

import UIKit
import GoogleMaps

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

    var window: UIWindow?
    let DBName = "test"
    var logFile: FileUtils?
    var viewController:ViewController?

    var count = 0
    var appOpenCount = 0
    let totalPath = GMSMutablePath()
    var leaveCoordinates = 0
    var previousLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
    var locationManager: CLLocationManager?
    var significatLocationManager : CLLocationManager?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        GMSServices.provideAPIKey("*********************")
        logFile = FileUtils(fileName: "\(DBName).txt")

        logFile!.appendFile("\n\nlaunchOptions : \(launchOptions)")

        let defaults = NSUserDefaults.standardUserDefaults()
        count = defaults.integerForKey("Enabled")
        appOpenCount = defaults.integerForKey("appOpenCount")

        if(UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Available){
            logFile!.appendFile("\nYessss")
        } else {
            logFile!.appendFile("\nNooo")
        }

        appOpenCount += 1
        defaults.setValue(appOpenCount, forKey: "appOpenCount")
        defaults.synchronize()

        if count == 0 {
            count += 1
            defaults.setValue(count, forKey: "Enabled")
            defaults.synchronize()
            Util.copyFile(count)
        }

        if let launchOpt = launchOptions{
            if (launchOpt[UIApplicationLaunchOptionsLocationKey] != nil) {
                logFile!.appendFile("\nExecuted on : significatLocationManager")
                self.significatLocationManager = CLLocationManager()
                self.significatLocationManager?.desiredAccuracy = kCLLocationAccuracyBest
                self.significatLocationManager?.delegate = self
                self.significatLocationManager?.requestAlwaysAuthorization()
                if #available(iOS 9.0, *) {
                    self.significatLocationManager!.allowsBackgroundLocationUpdates = true
                }
                self.significatLocationManager?.startUpdatingLocation()
            }else{
                logFile!.appendFile("\nExecuted on : locationManager1")
                self.locationManager = CLLocationManager()
                self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
                self.locationManager?.delegate = self
                self.locationManager?.requestAlwaysAuthorization()
                if #available(iOS 9.0, *) {
                    self.locationManager!.allowsBackgroundLocationUpdates = true
                }
                self.locationManager?.startUpdatingLocation()
            }
        } else {
            logFile!.appendFile("\nExecuted on : locationManager2")
            self.locationManager = CLLocationManager()
            self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
            self.locationManager?.delegate = self
            self.locationManager?.requestAlwaysAuthorization()
            if #available(iOS 9.0, *) {
                self.locationManager!.allowsBackgroundLocationUpdates = true
            }
            self.locationManager?.startUpdatingLocation()
        }
        return true
    }

    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
        let locationArray = locations as NSArray
        let newLocation = locationArray.lastObject as! CLLocation
        let coordinate = newLocation.coordinate

        let tempCoor = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
        let lat = tempCoor.latitude
        let lon = tempCoor.longitude
                insert(lat, lon: lon)
    }

    func applicationWillResignActive(application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        if self.significatLocationManager != nil {
            self.significatLocationManager?.startMonitoringSignificantLocationChanges()
        }else{
            self.locationManager?.startMonitoringSignificantLocationChanges()
        }
        logFile!.appendFile("\napplicationDidEnterBackground")
    }

    func applicationWillEnterForeground(application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }

    func insert(lat: Double, lon: Double){
        let locationInfo: LocationDetails = LocationDetails()
        locationInfo.latitude = lat
        locationInfo.longitude = lon
        locationInfo.time = NSDate()
        let db = "\(DBName)\(count).sqlite"
        ModelManager.getInstance(db).addLocationData(locationInfo)
    }
}

【问题讨论】:

    标签: ios swift location core-location


    【解决方案1】:

    问题 1

    使用 startMonitoringSignificantLocationChanges 与 startUpdatingLocation 有什么区别

    startUpdatingLocation在第一次调用时更新位置,然后在超过距离过滤器值时更新位置。

    它会在可用时使用 GPS,如果您持续使用它会耗尽您的电源/电池

    讨论

    此方法立即返回。调用此方法会导致 位置管理器获取初始位置修复(这可能需要 几秒钟)并通过调用其通知您的代表 locationManager:didUpdateLocations: 方法。之后,接收器主要在超过 distanceFilter 属性中的值时生成更新事件。不过,可能会在其他情况下提供更新。例如,如果硬件收集到更准确的位置读数,接收器可能会发送另一个通知。

    连续多次调用此方法不会自动 导致产生新的事件。在中调用 stopUpdatingLocation 然而,之间确实会导致下一个发送新的初始事件 调用此方法的时间。

    如果您启动此服务并且您的应用程序被暂停,则 系统停止传递事件,直到您的应用程序启动 再次运行(在前台或后台)。如果你的 应用程序终止,新位置事件的传递停止 共。因此,如果您的应用程序需要接收位置 在后台的事件,它必须包括 UIBackgroundModes 其 Info.plist 文件中的键(带有位置值)。

    除了你的委托对象实现 locationManager:didUpdateLocations: 方法,它也应该实现 locationManager:didFailWithError: 方法来响应潜在的 错误。

    startMonitoringSignificantLocationChanges 当位置发生重大变化时。

    它使用蜂窝或wifi,当它工作时,位置会改变或在特定的时间间隔内被调用。

    讨论

    该方法异步发起位置事件的传递, 你打电话后不久就回来了。位置事件被传递到 你的委托的 locationManager:didUpdateLocations: 方法。首先 要传递的事件通常是最近缓存的位置 事件(如果有),但在某些情况下可能是较新的事件。

    获取当前位置可能需要几秒钟的时间, 所以一定要检查你的位置事件的时间戳 委托方法。

    在返回当前位置修复后,接收器生成更新 仅当用户位置发生重大变化时才发生事件 检测到。例如,它可能会在设备 与不同的蜂窝塔相关联。它不依赖 distanceFilter 属性中的值以生成事件。打电话 此方法连续多次不会自动产生 在正在生成的新事件中。但是,在两者之间调用 stopMonitoringSignificantLocationChanges 下次您调用它时会发送一个新的初始事件 方法。

    如果您启动此服务并且您的应用程序随后 终止,系统自动重新启动应用程序到 新事件到达时的背景。在这种情况下,选项 字典传递给 locationManager:didUpdateLocations: 方法 您的应用程序委托包含密钥 UIApplicationLaunchOptionsLocationKey 来表明你的 由于位置事件而启动了应用程序。重启后, 您仍然必须配置一个位置管理器对象并调用它 方法继续接收位置事件。当你重新启动 位置服务,当前事件被传递给您的代表 立即地。此外,您所在位置的 location 属性 manager 对象甚至填充了最新的位置对象 在您开始定位服务之前。

    注意

    应用程序可以在设备移动 500 后立即收到通知 米或更多从其先前的通知。它不应该期望 通知的频率高于每五分钟一次。如果 设备能够从网络中检索数据,位置管理器 更有可能及时发送通知。

    出于不同的目的,我取自 here

    问题 2

    如果我们使用 startUpdatingLocation,它是否影响发布应用到 App Store

    2.16 被拒绝的一个可能原因是 iTunesConnect 中应用元数据的应用说明中没有 GPS 电池警告 - “继续使用 GPS 可能会缩短电池寿命”或类似的内容。

    更多关于this的信息

    问题 3

    当应用被杀死/暂停强制关闭被用户)时,需要一些时间从 AppDelegate 重新启动位置管理器这会导致位置数据丢失一段时间。任何可能的解决方案来克服这个

    不,我们不能结束,原因是新启动的记忆。

    【讨论】:

    • 很好的解释 Mr.@Anbu.Karthik 让我等待更多的答案并获得一个想法
    【解决方案2】:
    1. startMonitoringSignificantLocationChanges

    2. 开始更新位置

      • 在可用时使用 GPS
      • 非常准确,但耗电量很大

    通常,您可以使用 startUpdatingLocation 毫无问题地提交应用,只要您声明“继续使用在后台运行的 GPS 会显着降低电池寿命”。在应用程序说明上。如果您将背景模式设置为“位置”,则仅将其用于位置,不要做任何其他事情。

    您似乎没有使用beginBackgroundTaskWithExpirationHandler,我建议您在应用处于后台时使用它。

    显然,如果用户根本不希望您的应用获取位置,他们可以从设备设置中关闭 backgroundFetch,在这种情况下碰碰运气。

    【讨论】:

      猜你喜欢
      • 2019-08-03
      • 1970-01-01
      • 1970-01-01
      • 2015-08-04
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      • 2014-09-24
      • 2015-05-15
      相关资源
      最近更新 更多