Периодические обновления фонового расположения 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 ответов:
кажется,
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 каждый раз, когда приложение переключается между фоновым / фоновым режимом.
если у вас
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в течение нескольких секунд, чтобы получить высокую точность позиционирования. Я не уверен, Я никогда не использовал значительные услуги по изменению местоположения.
когда пришло время запустить службу определения местоположения и остановить фоновую задачу, фоновая задача должна быть остановлена с задержкой (1 секунды должно быть достаточно). В противном случае служба определения местоположения не запустится. Также услуга определения местоположения должна быть оставлена включенной на пару секунд (например, 3 секунды).
есть cocoapod APScheduledLocationManager что позволяет получать фоновые обновления местоположения каждый n секунды с требуемой точностью определения местоположения.
let manager = APScheduledLocationManager(delegate: self) manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)в репозиторий также содержит пример приложения, написанного в Swift 3.
Как насчет того, чтобы попробовать с
startMonitoringSignificantLocationChanges:API? Он определенно стреляет с меньшей частотой, и точность достаточно хороша. Кроме того, он имеет гораздо больше преимуществ, чем использование другихlocationManagerAPI.намного больше об этом API уже обсуждалось на этом ссылке
вам нужно добавить режим обновления местоположения в вашем приложении, добавив следующий ключ в вашей информации.файл plist.
<key>UIBackgroundModes</key> <array> <string>location</string> </array>
didUpdateToLocationметод будет вызван (даже если ваше приложение находится в фоновом режиме). Вы можете выполнить любую вещь на основе этого метода call
этот проект будет работать для управления местоположением в фоновом режиме.
будет управлять автоматически, если приложение входит в фоновом режиме и нужно отправить сетевой вызов для обновления местоположения.
после 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 } }