定期iOS背景位置更新

我正在写一个应用程序,需要高精度和低频率的背景位置更新。 该解决scheme似乎是一个后台NSTimer任务,启动位置pipe理器的更新,然后立即closures。 这个问题之前已经被问到:

如何在iOS应用程序中每隔n分钟更新一次后台位置?

应用程序转到后台后,每n分钟获取一次用户位置

iOS不是典型的后台位置跟踪计时器问题

具有“位置”后台模式的iOS长时间运行后台计时器

基于位置跟踪的iOS全职后台服务

但我还没有得到一个最低工作的例子 。 在对上述接受的答案进行了各种排列之后,我总结了一个起点。 进入背景:

- (void)applicationDidEnterBackground:(UIApplication *)application { self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ NSLog(@"ending background task"); [[UIApplication sharedApplication] endBackgroundTask:self.bgTask]; self.bgTask = UIBackgroundTaskInvalid; }]; self.timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self.locationManager selector:@selector(startUpdatingLocation) userInfo:nil repeats:YES]; } 

和委托方法:

 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { NSLog(@"%@", newLocation); NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining); [self.locationManager stopUpdatingLocation]; } 

当前的行为是backgroundTimeRemaining从180秒减less到零(在logging位置时),然后执行过期处理程序,并且不再生成位置更新。 如何修改上述代码以无限期地在后台接收定期的位置更新

更新 :我针对iOS 7,似乎有一些证据表明后台任务的行为有所不同:

从后台任务iOS 7中启动位置pipe理器

看来stopUpdatingLocation是触发后台看门狗定时器,所以我用didUpdateLocationreplace它:

 [self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers]; [self.locationManager setDistanceFilter:99999]; 

这似乎有效地closures了GPS。 后台NSTimer的select器变成:

 - (void) changeAccuracy { [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; [self.locationManager setDistanceFilter:kCLDistanceFilterNone]; } 

我所做的只是周期性地切换准确性,每隔几分钟就得到一个高精度的坐标,而且由于locationManager还没有停止, backgroundTimeRemaining保持其最大值。 在我的设备上,这样可以将电池消耗从每小时10%左右(后台持续kCLLocationAccuracyBest )降低到kCLLocationAccuracyBest %/小时。

我确实使用定位服务编写了一个应用程序,应用程序必须每隔10秒发送一次。 它工作得很好。

只需使用Apple的文档中的“ allowDeferredLocationUpdatesUntilTraveled:timeout ”方法即可。

步骤如下:

必需:注册后台模式更新位置。

1.创buildLocationManger和startUpdatingLocation,并根据需要精确和过滤距离:

 -(void) initLocationManager { // Create the manager object self.locationManager = [[[CLLocationManager alloc] init] autorelease]; _locationManager.delegate = self; // This is the most important property to set for the manager. It ultimately determines how the manager will // attempt to acquire location and thus, the amount of power that will be consumed. _locationManager.desiredAccuracy = 45; _locationManager.distanceFilter = 100; // Once configured, the location manager must be "started". [_locationManager startUpdatingLocation]; } 

2.为了保持应用程序在后台使用“allowDeferredLocationUpdatesUntilTraveled:timeout”方法永远运行,当应用程序移动到后台时,必须使用新参数重新启动updatesLocation,如下所示:

 - (void)applicationWillResignActive:(UIApplication *)application { _isBackgroundMode = YES; [_locationManager stopUpdatingLocation]; [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest]; [_locationManager setDistanceFilter:kCLDistanceFilterNone]; _locationManager.pausesLocationUpdatesAutomatically = NO; _locationManager.activityType = CLActivityTypeAutomotiveNavigation; [_locationManager startUpdatingLocation]; } 

3.应用程序正常使用“locationManager:didUpdateLocations:”callback获取updatedLocations:

 -(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { // store data CLLocation *newLocation = [locations lastObject]; self.userLocation = newLocation; //tell the centralManager that you want to deferred this updatedLocation if (_isBackgroundMode && !_deferringUpdates) { _deferringUpdates = YES; [self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10]; } } 

但是你应该为你的目的处理“locationManager:didFinishDeferredUpdatesWithError:”callback中的数据

 - (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error { _deferringUpdates = NO; //do something } 

5. 注意:我认为我们应该重置LocationManager的参数,每次应用程序切换后台/地面模式。

  1. 如果你的plist中有UIBackgroundModes ,那么你不需要使用beginBackgroundTaskWithExpirationHandler方法。 这是多余的。 你也正在使用它不正确(见这里 ),但这是因为你的plist设置。

  2. 使用plist中的UIBackgroundModes location ,只要CLLocationManger正在运行,应用程序将继续无限期地在后台运行。 如果您在后台调用stopUpdatingLocation ,则应用程序将停止并不会再次启动。

    也许你可以在调用stopUpdatingLocation之前调用stopUpdatingLocation ,然后在调用startUpdatingLocation ,可以调用endBackgroundTask以在GPS停止时保持它endBackgroundTask ,但是我从来没有试过 – 这只是一个想法。

    另一个选项(我没有尝试过)是让位置pipe理器在后台运行,但是一旦你得到一个准确的位置,将所需的desiredAccuracy属性改为1000米或更高,以允许GPS芯片closures(以节省电池) 。 然后在10分钟后,当您需要更新位置时,将所需的准确度更改回100m以打开GPS,直到您获得准确的位置,重复。

  3. 当您在地点经理上致电startUpdatingLocation时,您必须给予时间来获得职位。 您不应该立即调用stopUpdatingLocation 。 我们让它运行最多10秒,或者直到我们得到一个非caching的高精度位置。

  4. 您需要过滤掉caching的位置,并检查您获得的位置的准确性,以确保它符合您的最低要求的准确性(请参见此处 )。 你得到的第一个更新可能是10分钟或10天。 你得到的第一个精度可能是3000米。

  5. 考虑使用重要的位置更改API。 一旦你得到重要的变化通知,你可以启动CLLocationManager几秒钟以获得一个高准确度的位置。 我不确定,我从来没有使用过重要的位置更改服务。

当开始定位服务和停止后台任务的时候,后台任务应该停止一段时间(1秒就够了)。 否则位置服务将无法启动。 此外,位置服务应该保持ON几秒钟(例如3秒)。

有一个cocoapod APScheduledLocationManager ,允许每n秒获得所需的位置准确性的背景位置更新。

 let manager = APScheduledLocationManager(delegate: self) manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100) 

该库还包含一个用Swift 3编写的示例应用程序。

如何尝试startMonitoringSignificantLocationChanges: API? 它肯定会发生频率较低,准确性相当好。 另外它比使用其他locationManager API更有优势。

更多关于这个API已经在这个链接上讨论过了

您需要在您的应用程序中添加位置更新模式,方法是在您的info.plist中添加以下密钥。

 <key>UIBackgroundModes</key> <array> <string>location</string> </array> 

didUpdateToLocation方法将被调用(即使当你的应用程序在后台)。 你可以在这个方法调用的基础上执行任何事情

本项目将在后台pipe理位置。

位置背景更新

如果应用程序进入后台,将自动进行pipe理,并需要发送networking呼叫更新位置。