应用在后台时启动 CLLocationManager 位置更新
Posted
技术标签:
【中文标题】应用在后台时启动 CLLocationManager 位置更新【英文标题】:Starting CLLocationManager location updates while app is in background 【发布时间】:2016-08-30 22:55:12 【问题描述】:我正在构建一个将 CoreLocation 用于两个目的的应用:
首先,监控信标区域。大多数情况下,我们使用充当信标的特定硬件设备,并使用 CoreLocation 跟踪该信标连接的状态。
其次,当应用检测到与信标设备断开连接时,我们要启动 GPS 进程来定位断开连接的位置。
在大多数情况下,信标设备的断开回调发生在应用处于后台时。因此,应用必须在后台调用-startUpdatingLocation
来启动 GPS 跟踪。
这就是问题所在。如果在应用程序处于后台时发生-startUpdatingLocation
调用,ios 似乎不会启用位置后台更新。
显然,我已经正确配置了后台模式NSLocationAlwaysUsageDescription
,将属性_locationManager.allowsBackgroundLocationUpdates
设置为YES
。
令人惊奇的是:如果我在前台调用-startUpdatingLocation
然后切换到后台,则在后台时位置更新会正确发送。但是,当在后台调用 -startUpdatingLocation
时,应用程序会收到 2 或 3 个位置更新,然后永远暂停.. 直到我手动将应用程序带回前台,当位置更新恢复时,即使我切换也会继续工作应用程序再次进入后台。
有人知道或理解我应该怎么做才能解决这个问题吗?还是它是iOS的主要限制?由于我的应用程序的要求,我真的需要在应用程序处于后台时开始定位更新(在信标的回调中确实退出区域)。
顺便说一句,如果你想重现这个问题是很容易的。只需在 Xcode 中创建一个空白项目并将此代码添加到您的默认视图控制器。请记住启用后台模式,为您的 info.plist 键“NSLocationAlwaysUsageDescription”添加描述。
- (void)viewDidLoad
[super viewDidLoad];
_locationManager = [[CLLocationManager alloc] init];
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_8_3)
_locationManager.allowsBackgroundLocationUpdates = YES;
_locationManager.delegate = self;
_locationManager.activityType = CLActivityTypeOther;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
_locationManager.distanceFilter = kCLDistanceFilterNone;
[_locationManager requestAlwaysAuthorization];
// Creating a background task to delay the start of the startUpdatingLocation
__block UIBackgroundTaskIdentifier taskIdentifier = UIBackgroundTaskInvalid;
taskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
];
// Delaying the call to startUpdatingLocation 10 seconds, enough time to dismiss the application
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
[_locationManager startUpdatingLocation];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
);
);
然后,通过执行一些 NSLogs 来跟踪 CLLocationManager 委托,看看会发生什么。
谢谢,
【问题讨论】:
我遇到了同样的问题。就我而言,这些位置可能会运行很长时间,或者只需几秒钟然后停止,或者根本没有开始。我也尝试在后台启动位置时保持后台任务运行但没有运气...... 【参考方案1】:自您发布此问题以来已经 3 个月了。你自己想通了吗?
我试用了您的代码。我在AppDelegate
委托函数和viewDidLoad
中添加了一些NSLogs
。另外,在AppDelegate
类中,我退出了后台剩余时间。
如果在进入后台之前调用startUpdatingLocation
:
2016-08-18 01:22:52.355 test background GPS from StackOF[2267:745042] viewDidLoad
2016-08-18 01:22:52.376 test background GPS from StackOF[2267:745042] _locationManager.allowsBackgroundLocationUpdates = YES;
2016-08-18 01:22:52.382 test background GPS from StackOF[2267:745042] _locationManager requestAlwaysAuthorization
2016-08-18 01:22:52.410 test background GPS from StackOF[2267:745042] applicationDidBecomeActive
2016-08-18 01:22:52.469 test background GPS from StackOF[2267:745042] viewDidAppear
2016-08-18 01:23:03.361 test background GPS from StackOF[2267:745042] [_locationManager startUpdatingLocation];
2016-08-18 01:23:03.362 test background GPS from StackOF[2267:745042] start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
2016-08-18 01:23:03.382 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:23:03.630 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.709 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.716 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.720 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.814 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:23:05.004 test background GPS from StackOF[2267:745042] inner block [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
2016-08-18 01:23:05.005 test background GPS from StackOF[2267:745042] inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
2016-08-18 01:23:08.287 test background GPS from StackOF[2267:745042] applicationDidEnterBackground. Background time left: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
2016-08-18 01:23:10.319 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:23:19.321 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
/****
Keep on polling...
****/
如果进入后台后调用startUpdatingLocation
:
2016-08-18 01:34:29.023 test background GPS from StackOF[2285:747132] viewDidLoad
2016-08-18 01:34:29.030 test background GPS from StackOF[2285:747132] _locationManager.allowsBackgroundLocationUpdates = YES;
2016-08-18 01:34:29.033 test background GPS from StackOF[2285:747132] _locationManager requestAlwaysAuthorization
2016-08-18 01:34:29.047 test background GPS from StackOF[2285:747132] applicationDidBecomeActive
2016-08-18 01:34:29.104 test background GPS from StackOF[2285:747132] viewDidAppear
2016-08-18 01:34:31.612 test background GPS from StackOF[2285:747132] applicationDidEnterBackground. Background time left: 179.964144
2016-08-18 01:34:40.004 test background GPS from StackOF[2285:747132] [_locationManager startUpdatingLocation];
2016-08-18 01:34:40.006 test background GPS from StackOF[2285:747132] start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
2016-08-18 01:34:40.290 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 10.000000
2016-08-18 01:34:40.332 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.117789
2016-08-18 01:34:40.336 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.118536
2016-08-18 01:34:40.493 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.141209
2016-08-18 01:34:40.496 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.147748
2016-08-18 01:34:40.505 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.148158
2016-08-18 01:34:40.507 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:34:41.640 test background GPS from StackOF[2285:747132] inner block [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
2016-08-18 01:34:41.642 test background GPS from StackOF[2285:747132] inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
/****
stops polling...
****/
记下后台剩余时间。如果应用在 CLLocationManager 开始更新位置之前进入后台,则后台剩余时间为 3 分钟。我不太确定自己,因为我也有自己的 GPS 后台运行问题,但从这个简短的测试来看,似乎必须在应用程序进入后台之前调用 CLLocationManager。
这里是我放置 NSLogs 的地方:
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"viewDidLoad");
_locationManager = [[CLLocationManager alloc] init];
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_8_3)
_locationManager.allowsBackgroundLocationUpdates = YES;
NSLog(@"_locationManager.allowsBackgroundLocationUpdates = YES;");
_locationManager.delegate = self;
_locationManager.activityType = CLActivityTypeOther;
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_locationManager.distanceFilter = kCLDistanceFilterNone;
[_locationManager requestAlwaysAuthorization];
NSLog(@"_locationManager requestAlwaysAuthorization");
// Creating a background task to delay the start of the startUpdatingLocation
__block UIBackgroundTaskIdentifier taskIdentifier = UIBackgroundTaskInvalid;
taskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
NSLog(@"[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];");
];
// Delaying the call to startUpdatingLocation 10 seconds, enough time to dismiss the application
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
[_locationManager startUpdatingLocation];
NSLog(@"[_locationManager startUpdatingLocation];");
if(_locationManager.allowsBackgroundLocationUpdates == YES)
NSLog(@"start updating location, _locationManager.allowsBackgroundLocationUpdates == YES");
else
NSLog(@"start updating location, _locationManager.allowsBackgroundLocationUpdates == NO");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
NSLog(@"inner block [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];");
if(_locationManager.allowsBackgroundLocationUpdates == YES)
NSLog(@"inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == YES");
else
NSLog(@"inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == NO");
);
);
CLLocationManager
委托函数:
-(void)locationManager:(CLLocationManager *) manager didUpdateLocations:(nonnull NSArray<CLLocation *> *)locations
CLLocation *location = [locations firstObject];
NSLog(@"Lat/Long: %f %f, horizontal accuracy: %f",
location.coordinate.latitude,
location.coordinate.longitude,
location.horizontalAccuracy);
AppDelegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
NSLog(@"applicationDidEnterBackground. Background time left: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
TL;DR:尝试直接在 viewDidLoad
或 AppDelegate 中调用 startUpdatingLocation
application:didFinishLaunchingWithOptions:
【讨论】:
以上是关于应用在后台时启动 CLLocationManager 位置更新的主要内容,如果未能解决你的问题,请参考以下文章
重启后 Apple Watch 4 启动时自动启动后台 WatchOS 应用程序
应用在后台时启动 CLLocationManager 位置更新