Періодичні оновлення фонового розташування iOS


91

Я пишу програму, яка вимагає оновлення фонового розташування з високою точністю та низькою частотою . Рішенням, здається, є фонове завдання 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 із фонового завдання


Ви не згадуєте, на яку версію iOS ви націлюєтесь. iOS 7 (наприклад) має іншу багатозадачність, ніж попередня.
Джейсон Уайтхорн,

@JasonWhitehorn Хороший момент. Я оновив запитання.
розмова

2
@pcoving: Чи буде ваше рішення працювати, навіть якщо додаток буде вбито або припинено?
Нірав

Відповіді:


62

Здається, 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% на годину на моєму пристрої.


4
Не могли б ви оновити своє запитання на повному робочому прикладі? Щось на кшталт: "ОНОВЛЕННЯ: Я це зрозумів. Ось моє рішення ..."
smholloway

@Virussmca Я повернувся до попередньої відповіді, яка є набагато надійнішою з подібною продуктивністю. Зупинка оновлень місцезнаходжень, здається, викликає перегоновий стан (або якийсь інший недетермінований сценарій).
обговорення

Цей підхід не спрацює, як ви очікуєте: 1) Збільшення значення властивості distanceFilter не призводить до економії заряду, оскільки gps все ще повинен визначати ваше місцезнаходження. 2) У iOS 7 фоновий час не суміжний. 3) Ви можете взяти до уваги значні послугиChangeLocation. 4) Окрім вашого роздуму в iOS 7, ви не можете запустити UIBackgroundModes - розташування з фона ...
aMother

2
@aMother 1) Я усвідомлюю, що збільшення відстані фільтра (і не обробка зворотних викликів) не економить багато батареї. Однак setDesiredAccuracy робить! Це спирається на інформацію про стільникову вежу, яка в основному надходить безкоштовно. 2) Несуміжні? 3) Я напівжирним шрифтом зазначив, що мені потрібна висока точність. Значні зміни місцеположення цього не забезпечують. 4) Служби локації не запускаються / зупиняються з фонового режиму.
обговорення

2
Я знаю, що це старий пост зараз, але користувачі мого додатка, які використовують цей трюк, помітили значне збільшення використання акумулятора з моменту оновлення до iOS9. Я ще не досліджував, але у мене таке відчуття, що цей "фокус" може більше не працювати?
Гері Райт,

45

Я писав додаток за допомогою служб локації, додаток повинен надсилати місцезнаходження кожні 10 секунд. І це спрацювало дуже добре.

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

Кроки такі:

Потрібно: Зареєструвати фоновий режим для оновлення Розташування.

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" у фоновому режимі, потрібно перезапустити 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 кожного разу, коли програма перемикається між фоновим / фоновим режимом.


Це чудово працює, і я відчуваю, що це правильний спосіб зробити, як згадувалось у WWDC минулого року !! Дякую за допис
prasad1250,

@ samthui7 дякую, це працює, але мені також потрібно визначити місцезнаходження після припинення. як це може бути. ??
Мохіт Томар

@ Samthui7 .. дивіться цей Que ... stackoverflow.com/questions/37338466 / ...
Сураджа Sukale

2
@amar: Я також перевіряв бета-версію iOS 10, вона все ще працює. Зверніть увагу, що нам потрібно додати трохи коду перед startUpdatingLocation: allowsBackgroundLocationUpdates = YESта requestAlwaysAuthorizationабо requestWhenInUseAuthorization. Наприклад: `CLLocationManager * locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; locationManager.allowsBackgroundLocationUpdates = ТАК; [locationManager requestAlwaysAuthorization]; [locationManager startUpdatingLocation]; `
samthui7

1
@Nirav Ні. Я не думаю, що Apple могла б дозволити нам це зробити, наскільки мені відомо.
samthui7

38
  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на кілька секунд, щоб отримати позицію високої точності. Я не впевнений, я ніколи не користувався серйозними послугами зміни місця розташування.


CoreLocationЗдається, посилання Apple рекомендує використовувати beginBackgroundTaskWithExpirationHandlerпри обробці даних про місцезнаходження у фоновому режимі: "Якщо додатку iOS потрібно більше часу для обробки даних про місцезнаходження, він може попросити більше часу у фоновому режимі, використовуючи метод beginBackgroundTaskWithName: expirationHandler: метод класу UIApplication." - developer.apple.com/library/ios/documentation/UserExperience/…
eliocs

4
не забудьте додати _locationManager.allowsBackgroundLocationUpdates = ТАК; для iOS9 +, інакше програма перейде в режим сну через 180 секунд
kolyancz

@kolyancz Я щойно спробував його на iOS 10.1.1 для iPhone 6+. Приблизно ~ 17 хвилин пішло на сон і спрацювало locationManagerDidPauseLocationUpdates. Я перевірив цю поведінку 10 разів!
Мед,

Мені справді незрозуміло, чому ти кажеш, що це спірне питання, а потім ти кажеш, що ти повинен обернути його у фоновому режимі
Мед,

10

Коли настав час запустити службу локації та зупинити фонове завдання, фонове завдання слід зупинити із затримкою (достатньо 1 секунди). В іншому випадку служба локації не запускається. Також Службу локації слід залишити увімкненою на пару секунд (наприклад, 3 секунди).

Існує cocoapod APScheduledLocationManager, який дозволяє отримувати оновлення фонового розташування кожні n секунд із бажаною точністю розташування.

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

Сховище також містить приклад програми, написаної на Swift 3.


1

Як щодо спробувати startMonitoringSignificantLocationChanges:API? Це точно спрацьовує з меншою частотою, а точність досить гарна. Крім того, він має набагато більше переваг, ніж використання інших locationManagerAPI.

Набагато більше про цей API вже обговорювалося за цим посиланням


1
Я спробував такий підхід. Це порушує вимогу точності - згідно з документами : This interface delivers new events only when it detects changes to the device’s associated cell towers
випуск

1

Вам потрібно додати режим оновлення місцезнаходження у свою програму, додавши наступний ключ у свій список info.plist.

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

didUpdateToLocationбуде викликаний метод (навіть коли ваш додаток у фоновому режимі). Ви можете виконати будь-яку річ на основі виклику цього методу


Режим оновлення місцезнаходження вже є в списку info.plist. Як я вже згадував, я отримую оновлення до фонового таймауту 180 секунд.
розмова

0

Після iOS 8 відбулося кілька змін у фоновому завантаженні та оновленнях, здійснених Apple, у CoreLocation framework.

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
    }

}

0

Щоб користуватися стандартними службами розташування, поки програма перебуває у фоновому режимі, потрібно спочатку увімкнути фонові режими на вкладці Можливості налаштувань цілі та вибрати Оновлення місцезнаходження.

Або додайте його безпосередньо до 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

Завдання С

//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];

Стрімкий

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


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.