应用在后台时启动CLLocationManager位置更新

时间:2016-05-05 08:19:20

标签: ios background gps location core-location

我正在构建一个将CoreLocation用于两个目的的应用程序:

  1. 首先,监控信标区域。大多数情况下,我们使用一个充当信标的特定硬件设备,我们使用CoreLocation跟踪该信标连接的状态。

  2. 其次,当应用程序检测到与信标设备断开连接时,我们希望启动GPS进程以找到断开连接的位置。

  3. 在大多数情况下,当应用程序处于后台时,会发生信标设备的断开连接回调。因此,应用必须通过在后台调用-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];
            });
        });
    }
    

    然后,通过执行一些NSLog来跟踪CLLocationManager委托,看看会发生什么。

    谢谢,

1 个答案:

答案 0 :(得分:2)

你发布这个问题已经3个月了。你有没有想过自己?

我试用了你的代码。我在NSLogs委托函数和AppDelegate中添加了一些viewDidLoad。此外,在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。

以下是我放置NSLog的地方:

- (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 :尝试在startUpdatingLocation或AppDelegate viewDidLoad中调用application:didFinishLaunchingWithOptions: