iOS Swift – 用NSTimer重新加载位置function,为应用程序背景不起作用

我的位置服务有问题。 我不能设置一个函数,通过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() } 

PS。 – 当然,我已经导入了corelocation。 – 当我回到应用程序,控制台打印应该打印在背景中。

在应用程序处于后台时,不能使NSTimer像这样工作。 NSTimer不是“实时机制”。 从官方文档 :

定时器与运行循环一起工作。 要有效地使用定时器,你应该知道如何运行循环 – 参见NSRunLoop和Threading Programming Guide 。 请特别注意,运行循环保持对定时器的强引用,因此在将其添加到运行循环后,您不必保持自己对定时器的强引用。

计时器不是一个实时的机制, 只有在添加了定时器的运行循环模式之一运行并且能够检查定时器的触发时间是否已经过去时才会触发。 由于典型的运行循环pipe理的各种input源,定时器的时间间隔的有效分辨率被限制在大约50-100毫秒的量级。 如果在长时间标注期间发生定时器的触发时间, 或者运行循环处于未监视定时器的模式,定时器将在下次运行循环检查定时器时才触发 因此,计时器触发的实际时间可能是预定的点火时间之后的相当长的一段时间。

强调我的。

从这个重要的是,当你的应用程序在后台,你的计时器将被安排在任何运行循环不积极运行。

只要你的应用程序返回到前台,这个运行循环就会启动,看到你的计时器已经过期,并将消息发送给select器。


使用iOS 7并转发,如果要在后台执行操作,则可以告诉操作系统您要执行“后台提取”。

要设置它,我们必须先告诉操作系统我们想要获取数据的频率,所以在didFinishLaunching... ,添加以下方法:

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

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

现在,我们必须实现当OS给我们做后台工作时实际调用的方法:

 func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // do background work } 

我们可以在这个方法中做任何我们想做的事。 有两个渔获,但是。

  1. 这个方法在我们的应用程序在后台时被调用。 操作系统限制我们(我相信)三十秒。 三十秒后,我们的时间到了。
  2. 我们必须调用completionHandler() (或者操作系统会认为我们已经用完了所有的时间)。

传入的completionHandler接受一个枚举UIBackgroundFetchResult 。 我们应该通过它,或者.NoData.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) } } 

请记住,我们完全无法控制操作系统实际上让我们在后台运行此代码的频率。 操作系统使用几个指标来优化用户体验。

认为如果你的应用程序报告。完成处理程序,操作系统可能会给你第二次机会,但如果你滥用。 .Failed ,操作系统可能会黑名单使用后台提取您的应用程序(和苹果可以拒绝你的应用程序)。

如果你的应用程序不报告.NewData ,操作系统会让你的应用程序不太经常做后台工作。 我不是说这个,因为我build议你总是报告.NewData 。 你应该准确地报告。 操作系统在安排工作方面非常聪明。 如果在没有新数据的情况下传递.NewData ,操作系统会让您的应用程序工作得更多,这会耗尽用户的电池(可能会导致他们完全卸载您的应用程序)。

当您的应用程序开始执行后台工作时,还会涉及其他一些指标。 操作系统是不太可能让任何应用程序做后台工作,而用户正在使用他们的设备,而更有可能让应用程序做后台工作,而用户不使用他们的设备。 此外,操作系统更容易在WiFi上进行后台工作,同时插入某种充电器。

操作系统还会考虑用户使用您的应用程序的频率,或者经常使用您的应用程序的时间。 如果用户每天下午6点使用您的应用程序,并且从未在其他任何时间使用您的应用程序,那么最有可能的是您的应用程序将始终有机会在下午5:30至6点之间(即在用户使用应用程序之前) 从来没有在任何其他地方的一天。 如果用户很less使用您的应用程序,则可能需要几天,几周或几个月才能在后台工作。