Периодические обновления фонового расположения 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 из фоновой задачи

8 84

8 ответов:

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

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

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

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

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

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

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

шаги следующие:

требуется: Регистрация фонового режима для местоположения обновления.

1. создать LocationManger и startUpdatingLocation, с точностью и filteredDistance как все, что вы хотите:

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

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

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

3. приложение получает updatedLocations как обычно с "locationManager:didUpdateLocations:" обратный вызов:

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

5.Примечание: я думаю, что мы должны сбросить параметры LocationManager каждый раз, когда приложение переключается между фоновым / фоновым режимом.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

LOCATION BACKGROUND UPDATER

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

после iOS 8 их несколько изменений в CoreLocation framework, связанных с фоновой выборкой и обновлениями, сделанными 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's latitude
        print(location.coordinate.longitude) //will print user's longitude
        print(location.speed) //will print user's speed
    }

}