iOS Swift - 使用NSTimer for app background重新加载位置功能不起作用

Rob*_*scu 6 background core-location ios swift

我的位置服务有问题.我无法设置一个功能,通过NSTimer在后台更新我的位置坐标.这是来自appDelegate的代码:

var locationManager = CLLocationManager()

func applicationDidEnterBackground(application: UIApplication) {

    self.locationManager.delegate = self
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest

    self.theTimer = NSTimer(fireDate: NSDate(), interval: 40, target: self, selector: "handleTimer", userInfo: nil, repeats: true)
    NSRunLoop.currentRunLoop().addTimer(self.theTimer, forMode: NSDefaultRunLoopMode)

}

    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
    var locValue:CLLocationCoordinate2D = manager.location.coordinate
    println("dinBack = \(locValue.latitude) \(locValue.longitude)")
    self.locationManager.stopUpdatingLocation()
}

func handleTimer(){

    println("started")
    self.locationManager.startUpdatingLocation()

}
Run Code Online (Sandbox Code Playgroud)

PS. - 当然,我已经导入了重置. - 当我回到应用程序时,控制台会打印出应该在后台打印的内容.

nhg*_*rif 10

NSTimer当您的应用程序在后台时,您无法进行此类工作. NSTimer不是"实时机制".从官方文档:

定时器与运行循环一起使用.要有效地使用计时器,您应该了解运行循环的运行方式 - 请参阅NSRunLoop线程编程指南.请特别注意,运行循环保持对其计时器的强引用,因此在将计时器添加到运行循环后,您不必维护自己对计时器的强引用.

计时器不是实时机制; 只有当添加了计时器的其中一个运行循环模式正在运行并且能够检查计时器的触发时间是否已经过去时,它才会触发.由于典型的运行循环管理各种输入源,因此定时器的时间间隔的有效分辨率被限制在50-100毫秒的量级.如果在长时间标注期间或在运行循环处于不监视定时器的模式下发生定时器的触发时间,则定时器在下次运行循环检查定时器之前不会触发.因此,计时器可能发射的实际时间可能是在计划的发射时间之后的重要时间段.

强调我的.

重要的是,当您的应用程序在后台时,您的计时器将被安排的任何运行循环都没有正在运行.

一旦您的应用程序返回到前台,此运行循环将重新启动,发现您的计时器已过期,并将该消息发送到选择器.


对于iOS 7和转发,如果要在后台执行操作,可以告诉操作系统您要执行"后台提取".

要进行此设置,我们必须首先告诉操作系统我们想要获取数据的频率,因此didFinishLaunching...,请添加以下方法:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
    return true
}
Run Code Online (Sandbox Code Playgroud)

我们可以在这里传递任何时间间隔(例如,如果我们只想每天检查一次).但是,我们传入的值仅定义了应在检查之间传递的最小时间量.没有办法告诉操作系统检查之间的最长时间.

现在,我们必须实现在操作系统为我们提供后台工作机会时实际调用的方法:

func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
    // do background work
}
Run Code Online (Sandbox Code Playgroud)

我们可以在这种方法中做任何我们想做的事.然而,有两个渔获量.

  1. 当我们的应用程序在后台时调用此方法.操作系统限制我们(我相信)三十秒.三十秒后,我们的时间到了.
  2. 我们必须打电话给completionHandler()(或操作系统会认为我们使用了所有的时间).

completionHandler该获取需要一个枚举过去了,UIBackgroundFetchResult.我们应该传递它,或者.Failed,.NewData或者.NoData,取决于我们的实际结果(这种方法通常用于检查服务器的新数据).

所以,我们的方法可能如下所示:

func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
    // do stuff
    if let _ = error {
        completionHandler(.Failed)
    } else if results.count > 0 {
        completionHandler(.NewData)
    } else {
        completionHandler(.NoData)
    }
}
Run Code Online (Sandbox Code Playgroud)

请记住,我们有绝对的在OS的频率实际上将让我们在后台运行该代码控制.操作系统使用多个指标来优化用户体验.

如果你的应用程序.Failed向完成处理程序报告,操作系统可能会很快给你第二次机会,但是如果你滥用.Failed,操作系统可能会将你的应用程序列入黑名单(使用后台提取)(Apple 可能会拒绝你的应用程序).

如果您的应用没有报告.NewData,操作系统会让您的应用减少后台工作.我不是这样说的,因为我建议你总是报告.NewData.你一定要准确报道.操作系统在调度工作方面非常聪明.如果您在.NewData没有新数据的情况下通过,操作系统将让您的应用程序更频繁地工作,这将更快地耗尽用户的电池(并可能导致他们完全卸载您的应用程序).

但是,当您的应用开展后台工作时,还会涉及其他指标.当用户积极使用他们的设备时,操作系统不太可能让任何应用程序进行后台工作,并且当用户不使用他们的设备时,它更有可能让应用程序进行后台工作.此外,操作系统更有可能在WiFi上进行后台工作,同时将其插入某种充电器.

操作系统还会查看用户定期使用应用程序的频率,或者定期使用应用程序的时间.如果用户每天下午6点使用您的应用程序,而且从未在任何其他时间使用您的应用程序,那么您的应用程序很可能总是有机会在下午5:30至下午6点(在用户使用该应用程序之前)进行后台工作.从来没有在一天的任何其他部分.如果用户很少使用您的应用,那么在后台工作的机会可能是几天,几周或几个月.