Периодические обновления фона iOS
Я пишу приложение, которое требует фоновых обновлений местоположения с высокой точностью и низкой частотой. Решением, похоже, является фоновая задача NSTimer, которая запускает обновления диспетчера местоположений, которые затем немедленно закрываются. Этот вопрос был задан ранее:
Как мне получать фоновое обновление местоположения каждые n минут в моем приложении iOS?
iOS Не типичная проблема таймера фонового отслеживания местоположения
Длительный фоновый таймер iOS с фоновым режимом "location"
Постоянный фоновый сервис 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, и есть некоторые доказательства того, что фоновые задачи ведут себя по-разному:
8 ответов
Кажется, что 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% в час на моем устройстве.
Я написал приложение, используя службы определения местоположения, приложение должно отправлять местоположение каждые 10 секунд. И это сработало очень хорошо.
Просто используйте метод allowDeferredLocationUpdatesUntilTraveled:timeout, следуя документу Apple.
Шаги следующие:
Обязательно: зарегистрировать фоновый режим для обновления Location.
1. Создайте LocationManger и startUpdatingLocation, с точностью и отфильтрованным расстоянием, как хотите:
-(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. Приложение получает обновленные местоположения как обычно с обратным вызовом "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
в вашем списке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 секунды).
Есть кокосовый модуль https://github.com/paleksandrs/APScheduledLocationManager, который позволяет получать фоновые обновления местоположения каждые n секунд с желаемой точностью определения местоположения.
let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)
Хранилище также содержит пример приложения, написанного на Swift 3.
Как насчет того, чтобы попробовать startMonitoringSignificantLocationChanges:
API? Это определенно стреляет с меньшей частотой, и точность довольно хорошая. Кроме того, он имеет гораздо больше преимуществ, чем использование других locationManager
API.
Подробнее об этом API уже обсуждалось по этой ссылке
Вам нужно добавить режим обновления местоположения в вашем приложении, добавив следующий ключ в ваш info.plist.
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
didUpdateToLocation
будет вызван метод (даже если ваше приложение находится в фоновом режиме). Вы можете выполнить любую вещь на основе этого вызова метода
Чтобы использовать стандартные службы определения местоположения, когда приложение работает в фоновом режиме, вам необходимо сначала включить фоновые режимы на вкладке "Возможности" в настройках цели и выбрать "Обновления местоположения".
Или добавьте его прямо в 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()
Ссылка:https://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba
После 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's latitude
print(location.coordinate.longitude) //will print user's longitude
print(location.speed) //will print user's speed
}
}
Этот проект будет работать для управления местоположением в фоновом режиме.
будет работать автоматически, если приложение переходит в фоновый режим и ему необходимо отправить сетевой вызов для обновления местоположения.