Запуск обновлений местоположения CLLocationManager, когда приложение находится в фоновом режиме

Я создаю приложение, которое использует CoreLocation для двух целей:

  • Во-первых, мониторинг регионов маяков. В основном мы работаем с определенным аппаратным устройством, которое действует как маяк, и мы отслеживаем с помощью CoreLocation статус этого маякового соединения.

  • Во-вторых, когда приложение обнаруживает отсоединение от маякового устройства, мы хотим начать процесс GPS, чтобы найти место, где произошло разъединение.

В большинстве случаев обратный вызов разъединения маякового устройства происходит, когда приложение находится в фоновом режиме. Поэтому приложение должно начать отслеживание GPS, вызывая -startUpdatingLocation, находясь в фоновом режиме.

И это проблема. Похоже, что iOS не позволяет обновлять данные о местоположении, если вызов -startUpdatingLocation произошел, когда приложение было в фоновом режиме.

Очевидно, что я правильно настроил фоновые режимы, NSLocationAlwaysUsageDescription, установил свойство _locationManager.allowsBackgroundLocationUpdates на YES.

Удивительная вещь: если я назову -startUpdatingLocation, находясь на переднем плане, а затем переключись на фон, обновления местоположения будут отправляться правильно, когда они находятся в фоновом режиме. Однако при вызове -startUpdatingLocation в фоновом режиме приложение получает 2 или 3 обновления местоположения, а затем останавливается навсегда.. пока я вручную не верну приложение на передний план, когда обновления местоположений возобновятся, а затем продолжайте работать, даже если я переключу приложение к фону снова.

Кто-нибудь знает или понимает, что я должен сделать, чтобы исправить это? или это основное ограничение iOS? Из-за требований моего приложения мне действительно нужно начать поиск обновлений, когда приложение находится в фоновом режиме (внутри обратного вызова маяка была выходная область).

Btw, если вы хотите воспроизвести эту проблему, очень просто. Просто создайте пустой проект в 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];
        });
    });
}

Затем отследите делегат CLLocationManager, выполнив некоторые NSLogs и узнайте, что произойдет.

Спасибо,

Ответ 1

прошло 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 должен был быть вызван до того, как приложение перейдет в фоновый режим.

Здесь я помещаю свои 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: попробуйте позвонить startUpdatingLocation прямо в viewDidLoad или в AppDelegate application:didFinishLaunchingWithOptions: