Периодические обновления местоположения фона iOS

Я пишу приложение, которое требует обновлений фонового местоположения с высокой точностью и низкой частотой. Решение кажется фоновой задачей NSTimer, которая запускает обновления диспетчера местоположений, которые затем немедленно завершаются. Этот вопрос задан раньше:

Как получить обновление фонового местоположения каждые n минут в приложении iOS?

Получение местоположения пользователя каждые 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 секунд до нуля (в то время как место регистрации), а затем выполняется обработчик истечения срока действия и никаких дополнительных обновлений местоположения не генерируется. Как изменить приведенный выше код, чтобы получать периодические обновления местоположения в фоновом режиме неопределенно?

Обновление. Я нацелен на iOS 7, и, как представляется, есть некоторые доказательства того, что фоновые задачи ведут себя по-другому:

Запустить диспетчер местоположений в iOS 7 из фоновой задачи

Ответ 1

Кажется, что stopUpdatingLocation является тем, что запускает контрольный таймер фона, поэтому я заменил его в didUpdateLocation на:

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

который, по-видимому, эффективно отключает GPS. Селектор фона NSTimer затем становится:

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

Все, что я делаю, периодически переключает точность, чтобы получить высокоточную координату каждые несколько минут, а поскольку locationManager не остановлен, backgroundTimeRemaining остается на своем максимальном значении. Это уменьшает потребление батареи от ~ 10% в час (с постоянным kCLLocationAccuracyBest в фоновом режиме) до ~ 2% в час на моем устройстве.

Ответ 2

Я написал приложение с помощью служб местоположения, приложение должно отправить местоположение каждые 10 секунд. И это сработало очень хорошо.

Просто используйте метод allowDeferredLocationUpdatesUntilTraveled: timeout", следующий за документом Apple.

Шаги выглядят следующим образом:

Требуется: Зарегистрировать фоновый режим для местоположения обновлений.

1. Создать LocationManger и startUpdatingLocation с точностью и фильтромDistance как угодно:

-(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, вы должны перезапустить updateLocation с новым параметром, когда приложение перемещается в фоновый режим, например:

- (void)applicationWillResignActive:(UIApplication *)application {
     _isBackgroundMode = YES;

    [_locationManager stopUpdatingLocation];
    [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [_locationManager setDistanceFilter:kCLDistanceFilterNone];
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [_locationManager startUpdatingLocation];
 }

3. Приложение обновляетсяLocations как обычно с "locationManager: didUpdateLocations:" callback:

-(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];
    }
}

4. Но вы должны обрабатывать данные, а затем обратный вызов "locationManager: didFinishDeferredUpdatesWithError:" для вашей цели

- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {

     _deferringUpdates = NO;

     //do something 
}

. ПРИМЕЧАНИЕ. Я думаю, что мы должны reset параметры LocationManager при каждом переключении между фоновым/наземным режимом.

Ответ 3

  • Если у вас есть UIBackgroundModes в вашем plist с ключом location, вам не нужно использовать метод beginBackgroundTaskWithExpirationHandler. Это лишнее. Кроме того, вы используете его неправильно (см здесь), но это спорный вопрос, так как ваш PLIST установлен.

  • С UIBackgroundModes location в plist приложение будет продолжать работать в фоновом режиме неограниченно до тех пор, пока работает CLLocationManger. Если вы назовете stopUpdatingLocation в фоновом режиме, приложение остановится и не запустится снова.

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

    Другой вариант (который я не пробовал) заключается в том, чтобы сохранить менеджер местоположения в фоновом режиме, но как только вы получите точное изменение местоположения, свойство desiredAccuracy на 1000 м или выше, чтобы позволить чипу GPS отключиться (для экономии заряда аккумулятора). Затем через 10 минут, когда вам потребуется другое обновление местоположения, измените desiredAccuracy на 100 м, чтобы включить GPS, пока не получите точное местоположение, повторите.

  • Когда вы вызываете startUpdatingLocation в диспетчере местоположений, вы должны дать ему время, чтобы получить позицию. Вы не должны сразу звонить stopUpdatingLocation. Мы позволяем ему работать максимум 10 секунд или пока не получим не кэшированное местоположение с высокой точностью.

  • Вам нужно отфильтровать места в кешировании и проверить точность местоположения, которое вы получите, чтобы убедиться, что оно соответствует вашей минимально необходимой точности (см. здесь). Первое обновление, которое вы получите, может составлять 10 минут или 10 дней. Первая точность, которую вы получите, может составлять 3000 м.

  • Вместо этого рассмотрите возможность использования существенных API изменений местоположения. Как только вы получите уведомление о значительных изменениях, вы можете запустить CLLocationManager на несколько секунд, чтобы получить положение с высокой точностью. Я не уверен, я никогда не использовал значительные службы изменения местоположения.

Ответ 4

Когда пришло время запуска службы определения местоположения и остановки фоновой задачи, фоновая задача должна быть остановлена ​​с задержкой (1 секунда должна быть достаточной). В противном случае служба поиска не запускается. Также Location Service должен оставаться включенным на пару секунд (например, 3 секунды).

Существует cocoapod APScheduledLocationManager, который позволяет получать обновления фонового местоположения каждые n секунд с желаемой точностью местоположения.

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

В репозитории также содержится пример приложения, написанного в Swift 3.

Ответ 5

Как насчет того, чтобы попробовать с помощью startMonitoringSignificantLocationChanges: API? Он определенно срабатывает с меньшей частотой, и точность достаточно хорошая. Кроме того, он имеет гораздо больше преимуществ, чем использование других API locationManager.

Больше об этом API уже обсуждалось на этой ссылке

Ответ 6

Вам нужно добавить режим обновления местоположения в ваше приложение, добавив следующий ключ в ваш info.plist.

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

didUpdateToLocation будет вызван метод (даже если ваше приложение находится в фоновом режиме). Вы можете выполнить любую вещь на основе этого вызова метода

Ответ 7

Этот проект будет работать для управления местоположением в фоновом режиме.

РАСПОЛОЖЕНИЕ ПРЕДПОСЫЛКИ UPDATER

автоматически будет работать, если приложение войдет в фоновый режим и ему необходимо отправить сетевой вызов для обновления местоположения.

Ответ 8

После iOS 8 у них есть несколько изменений в исходной выборке и обновлениях, связанных с базой CoreLocation, сделанных apple.

1) Apple представляет запрос AlwaysAuthorization для получения обновления местоположения в приложении.

2) Apple представила backgroundLocationupdates для получения местоположения из фона в iOS.

Для выбора местоположения в фоновом режиме вам необходимо включить обновление местоположения в возможностях Xcode.

//
//  ViewController.swift
//  DemoStackLocation
//
//  Created by iOS Test User on 02/01/18.
//  Copyright © 2018 iOS Test User. All rights reserved.
//

import UIKit
import CoreLocation

class ViewController: UIViewController {

    var locationManager: CLLocationManager = CLLocationManager()
    override func viewDidLoad() {
        super.viewDidLoad()
        startLocationManager()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    internal func startLocationManager(){
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startUpdatingLocation()
    }

}

extension ViewController : CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
        let location = locations[0] as CLLocation
        print(location.coordinate.latitude) // prints user latitude
        print(location.coordinate.longitude) //will print user longitude
        print(location.speed) //will print user speed
    }

}

Ответ 9

Чтобы использовать стандартные службы определения местоположения, когда приложение находится в фоновом режиме, необходимо сначала включить фоновые режимы на вкладке "Возможности" в настройках цели и выбрать "Обновления местоположения".

Или добавьте его непосредственно в Info.plist.

<key>NSLocationAlwaysUsageDescription</key>
 <string>I want to get your location Information in background</string>
<key>UIBackgroundModes</key>
 <array><string>location</string> </array>

Затем вам нужно настроить CLLocationManager

Цель C

//The Location Manager must have a strong reference to it.
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
//Request Always authorization (iOS8+)
if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { [_locationManager requestAlwaysAuthorization];
}
//Allow location updates in the background (iOS9+)
if ([_locationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) { _locationManager.allowsBackgroundLocationUpdates = YES;
}
[_locationManager startUpdatingLocation];

Swift

self.locationManager.delegate = self
if #available (iOS 8.0,*) {
    self.locationManager.requestAlwaysAuthorization()
}
if #available (iOS 9.0,*) {
    self.locationManager.allowsBackgroundLocationUpdates = true
}
self.locationManager.startUpdatingLocation()

Ref:https://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba

Ответ 10

locationManager.allowsBackgroundLocationUpdates = true