Як я можу отримувати фонове оновлення місцеположення кожні n хвилин у програмі iOS?


207

Я шукаю спосіб отримати фонове оновлення місцезнаходження кожні n хвилин у моєму додатку iOS. Я використовую iOS 4.3, і рішення повинно працювати для iPhone, що не входять у в'язницю.

Я спробував / розглядав наступні варіанти:

  • CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges: Це працює у фоновому режимі, як очікувалося, на основі налаштованих властивостей, але, здається, неможливо змусити його оновлювати місцезнаходження кожні n хвилин
  • NSTimer: Чи працює, коли додаток працює на передньому плані, але, здається, не призначений для фонових завдань
  • Локальні сповіщення: Місцеві сповіщення можуть бути заплановані кожні n хвилин, але неможливо виконати деякий код, щоб отримати поточне місцеположення (без того, щоб користувач запускав додаток через сповіщення). Цей підхід також не є чистим підходом, оскільки це не те, для чого слід використовувати сповіщення.
  • UIApplication:beginBackgroundTaskWithExpirationHandler: Наскільки я розумію, це слід використовувати для завершення певної роботи у фоновому режимі (також обмеженого у часі), коли додаток переміщується на задній план, а не для здійснення "тривалих" фонових процесів.

Як я можу реалізувати ці регулярні оновлення фонового місцезнаходження?



корисне спостереження: stackoverflow.com/questions/10235203/…
Лоло

1
Якщо ви намагаєтеся змусити його працювати на iOS 7, ви можете спробувати це рішення тут: stackoverflow.com/questions/18946881/… Якщо у вас виникли запитання, ви можете приєднатися до нас для обговорення тут: mobileoop.com/background -локація-оновлення-програмування-для-ios-7
Ricky

1
Усі ваші висновки правильні (чотири кулі). Цінні відомості, що знаходяться тоді, знаючи, що не відповідає вашому випадку? І так, коли у режимі СУПРЕНДЕННОГО РЕЖИМУ чи НЕ РАБОТИ, то немає остаточного методу оновлення кожні n хвилин.
LenArt

Відповіді:


113

Я знайшов рішення здійснити це за допомогою Форумів розробників Apple:

  • Вкажіть location background mode
  • Створіть NSTimerу фоновому режимі за допомогоюUIApplication:beginBackgroundTaskWithExpirationHandler:
  • Коли nце менше , ніж UIApplication:backgroundTimeRemainingвін буде працювати нормально. Коли nце більше , то location managerмає бути включено (і відключений) знову до того , як не залишився , щоб уникнути фонового завдання часу було вбито.

Це працює, оскільки розташування є одним із трьох дозволених типів фонового виконання .

Примітка. Я втратив деякий час, тестуючи це в тренажері, де він не працює. Однак він чудово працює на моєму телефоні.


24
чи трапляється у вас посилання на форум. Я хочу здійснити однотипне відображення місцеположення, і мені не вдалося змусити його працювати. Або якийсь зразок кодексу буде дуже вдячний.
utahwithak

4
Чи можете ви пояснити, чому фонове завдання не вбивається через 10 хвилин (максимально дозволений час), якщо ви просто зупинитесь і запустите менеджер локації? це якась призначена функціональність? це звучить більше як помилка в Apple SDK, якщо так трапляється. В якій версії iOS ви її пробували?
saurabh

5
@all: Так, наша програма доступна в AppStore. Я не збираюся розміщувати весь код, усі згадані пункти показують чітко задокументовані функції. Якщо у вас виникає конкретна проблема, опублікуйте власне запитання, пояснивши, що ви спробували, а що пішло не так.
wjans

3
@ user836026: Так, саме це я маю на увазі, визначаючи фоновий режим. Після припинення оновлення місцеположення його слід запустити знову протягом 10 хвилин, щоб уникнути припинення роботи програми.
wjans

12
Для всіх зацікавлених у тому, щоб побачити якийсь фактичний код, який відображає те, що тут обговорюється, перегляньте stackoverflow.com/questions/10235203/…
Лоло

55

Для iOS 8/9/10 для оновлення фонового розташування кожні 5 хвилин виконайте наступне:

  1. Перейдіть до проекту -> Можливості -> Фонові режими -> виберіть Оновлення місцеположення

  2. Перейдіть до проекту -> Інформація -> додайте ключ NSLocationAlwaysUsageDescription з порожнім значенням (або необов'язково будь-яким текстом)

  3. Щоб розташування працювало, коли ваша програма знаходиться у фоновому режимі, і надсилайте координати веб-сервісу або робити що-небудь з ними кожні 5 хвилин, виконайте це, як у наведеному нижче коді.

Я не використовую жодних фонових завдань чи таймерів. Я перевірив цей код на своєму пристрої з iOS 8.1, який лежав на моєму столі кілька годин, коли моя програма працює у фоновому режимі. Пристрій було заблоковано, і код постійно працював належним чином.

@interface LocationManager () <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSDate *lastTimestamp;

@end

@implementation LocationManager

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        LocationManager *instance = sharedInstance;
        instance.locationManager = [CLLocationManager new];
        instance.locationManager.delegate = instance;
        instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life
        instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important
    });

    return sharedInstance;
}

- (void)startUpdatingLocation
{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if (status == kCLAuthorizationStatusDenied)
    {
        NSLog(@"Location services are disabled in settings.");
    }
    else
    {
        // for iOS 8
        if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        {
            [self.locationManager requestAlwaysAuthorization];
        }
        // for iOS 9
        if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
        {
            [self.locationManager setAllowsBackgroundLocationUpdates:YES];
        }

        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *mostRecentLocation = locations.lastObject;
    NSLog(@"Current location: %@ %@", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude));

    NSDate *now = [NSDate date];
    NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0;

    if (!self.lastTimestamp || interval >= 5 * 60)
    {
        self.lastTimestamp = now;
        NSLog(@"Sending current location to web service.");
    }
}

@end

3
Це працює більше кількох годин? Схоже, навіть якщо додаток не припинено, пристрій перестає надсилати оновлення місцеположення через годину або близько того, що додаток перейде на другий план.
Мікстік

2
Вибір фонового режиму в моєму проекті вимкнено (це не має значення, вимкнено чи вимкнено). Однак паузиLocationUpdatesAutomatically повинні бути встановлені на NO для вищезгаданого прикладу для належної роботи. Він НЕ відновиться автоматично, коли ви знову почнете рухатися, якщо він був призупинений системою раніше. Ось чому у наведеному вище прикладі я встановив цю властивість як NO.
Лешек Сзарі

1
Я думаю, ви абсолютно праві. Знайдено більше інформації про проблему тут: stackoverflow.com/q/17484352/1048331
Myxtic

2
@LeszekS вам потрібно додати код нижче для підтримки iOS 9 для фону - if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) { [self.locationManager setAllowsBackgroundLocationUpdates:YES]; }
Fenil

2
Яка зручність робити це? Ви все ще споживаєте акумулятор, постійно постійну високу точність. Єдине, що ти не робиш - це ти фактично не використовуєш уже отримане місцезнаходження, поки не досягнеш інтервалу в 5 хвилин ...
Honey

34

Це я зробив у програмі, яку розробляю. Таймери не працюють, коли програма знаходиться у фоновому режимі, але програма постійно отримує оновлення місцеположення. Я читав десь у документації (я не можу знайти її зараз, я опублікую оновлення, коли я це роблю), що метод можна викликати лише в активному циклі запуску, коли програма знаходиться у фоновому режимі. У делегата програми є активний цикл запуску навіть у bg, тому вам не потрібно створювати власних, щоб зробити цю роботу. [Я не впевнений, чи це правильне пояснення, але ось як я зрозумів з того, що я читав]

Перш за все, додайте locationоб'єкт для ключа UIBackgroundModesв інформаційному списку програми. Тепер, що вам потрібно зробити, це запустити оновлення місця в будь-якій точці вашої програми:

    CLLocationManager locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;//or whatever class you have for managing location
    [locationManager startUpdatingLocation];

Далі напишіть спосіб обробки оновлень місцеположення, скажімо -(void)didUpdateToLocation:(CLLocation*)location, в делегаті програми. Потім реалізувати метод locationManager:didUpdateLocation:fromLocationв CLLocationManagerDelegateв класі , в якому ви почали менеджер розташування (оскільки ми встановили місцезнаходження менеджер делегат «я»). Всередині цього методу потрібно перевірити, чи не минув часовий інтервал, після якого потрібно обробити оновлення місцеположення. Це можна зробити, зберігаючи поточний час кожного разу. Якщо цей час минув, зателефонуйте методу UpdateLocation від делегата програми:

NSDate *newLocationTimestamp = newLocation.timestamp;
NSDate *lastLocationUpdateTiemstamp;

int locationUpdateInterval = 300;//5 mins

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
if (userDefaults) {

        lastLocationUpdateTiemstamp = [userDefaults objectForKey:kLastLocationUpdateTimestamp];

        if (!([newLocationTimestamp timeIntervalSinceDate:lastLocationUpdateTiemstamp] < locationUpdateInterval)) {
            //NSLog(@"New Location: %@", newLocation);
            [(AppDelegate*)[UIApplication sharedApplication].delegate didUpdateToLocation:newLocation];
            [userDefaults setObject:newLocationTimestamp forKey:kLastLocationUpdateTimestamp];
        }
    }
}

Це закликає ваш метод кожні 5 хвилин, навіть коли ваша програма знаходиться у фоновому режимі. Imp: Ця реалізація розряджає акумулятор, якщо точність даних про ваше місцезнаходження не є критичною, яку слід використовувати[locationManager startMonitoringSignificantLocationChanges]

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


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

3
Ви можете встановити точність диспетчера місцеположення до 1 км - це дозволить акумулятору майже бути недоторканим. Через 5 хв ви встановили точність у 1м. Коли ви отримаєте задоволене місце розташування (як правило, через 5 с), просто встановіть точність назад на 1 км.
knagode

knagode, я спробував запропоноване рішення щодо проблеми з розрядкою акумулятора, але навіть після збільшення точності через N хвилин метод locationManager: didUpdateLocations знову не викликається. Я спробував запустити оновлення та зупинити оновлення, замість збільшення та зменшення точності він називається успішно делегувати locationManager: метод didUpdateLocations через N хвилин, але не працює у фоновому режимі ...
Hardik Darji

Що стосується посилань документації. Дивіться тут : "Методи вашого об'єкта-делегата викликаються з потоку, в якому ви запустили відповідні служби локації. Цей потік сам повинен мати активний цикл запуску, як той, що знаходиться в головному потоці вашої програми."
Мед

24

Тепер, коли iOS6 є найкращим способом мати вічно працюючі служби локації, це ...

- (void)applicationWillResignActive:(UIApplication *)application
{
/*
 Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
 Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
 */

NSLog(@"to background");

app.isInBackground = TRUE;

UIApplication *app = [UIApplication sharedApplication];

// Request permission to run in the background. Provide an
// expiration handler in case the task runs long.
NSAssert(bgTask == UIBackgroundTaskInvalid, nil);

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    // Synchronize the cleanup call on the main thread in case
    // the task actually finishes at around the same time.
    dispatch_async(dispatch_get_main_queue(), ^{

        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    locationManager.distanceFilter = 100;
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    [locationManager startMonitoringSignificantLocationChanges];
    [locationManager startUpdatingLocation];

    NSLog(@"App staus: applicationDidEnterBackground");
    // Synchronize the cleanup call on the main thread in case
    // the expiration handler is fired at the same time.
    dispatch_async(dispatch_get_main_queue(), ^{
        if (bgTask != UIBackgroundTaskInvalid)
        {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }
    });
});

NSLog(@"backgroundTimeRemaining: %.0f", [[UIApplication sharedApplication] backgroundTimeRemaining]);

}

Просто випробували його так:

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

НЕ ЗАБУДУЙТЕ ВИКОРИСТАННЯ нових служб локації в iOS6

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{   
    CLLocation *loc = [locations lastObject];

    // Lat/Lon
    float latitudeMe = loc.coordinate.latitude;
    float longitudeMe = loc.coordinate.longitude;
}

1
Якщо додаток виходить з ладу або вбивається, система не перезапускається, правда?
Кен

1
Для отримання більш читаного коду ви можете зробити [location lastObject]; замість [location locationAtIndex: [count location] - 1]
axello

ваш метод лише на ios6?
pengwang

Чи треба цим користуватися? Я думав, що просто мати код керування місцем розташування у viewDidLoad класу, і встановив фоновий ключ у файлі plist, що програма реєструється для оновлень місцеположення, повинна бути достатньо хорошою? Чи можете ви мені допомогти з цим!
nithinreddy

Так, він буде працювати як шарм нітредредді, але він припинить роботу через 10 хвилин, оскільки iOS вбиває довгі нитки після цього періоду. Моє рішення є ідеальним, якщо ви хочете, щоб ці послуги були запущені назавжди. Я робив кілька тестів два дні тому, і він щомісяця заряджає 6% акумулятора. Використання [locationManager startUpdatingLocation] замість цього вичерпає 17%
Алехандро Луенго

13

До когось іншого кошмару з'ясуйте цей. У мене просте рішення.

  1. подивіться цей приклад з raywenderlich.com -> майте зразок коду, це прекрасно працює, але, на жаль, немає таймера під час розташування фону. це буде працювати нескінченно.
  2. Додати таймер за допомогою:

    -(void)applicationDidEnterBackground {
    [self.locationManager stopUpdatingLocation];
    
    UIApplication*    app = [UIApplication sharedApplication];
    
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    
     self.timer = [NSTimer scheduledTimerWithTimeInterval:intervalBackgroundUpdate
                                                  target:self.locationManager
                                                selector:@selector(startUpdatingLocation)
                                                userInfo:nil
                                                 repeats:YES];
    
    }
  3. Просто не забудьте додати "Реєстри додатків для оновлень місцеположення" у info.plist.


Чи отримує це місце більше 3 хвилин?
SleepNot

Потрібно встановити розташування в "Можливості" -> "Фоновий режим".
Серхіо Андреотті

Не працює!! ? Я перевірив на iOS 8 і iOS 10
Кірті

Виправте мене, якщо я помиляюся. На основі того, що я прочитав, ви запускаєте locationManager лише один раз. Після цього всі інтервали є зайвими. Оскільки це вже було розпочато
Мед

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

8

Ось що я використовую:

import Foundation
import CoreLocation
import UIKit

class BackgroundLocationManager :NSObject, CLLocationManagerDelegate {

    static let instance = BackgroundLocationManager()
    static let BACKGROUND_TIMER = 150.0 // restart location manager every 150 seconds
    static let UPDATE_SERVER_INTERVAL = 60 * 60 // 1 hour - once every 1 hour send location to server

    let locationManager = CLLocationManager()
    var timer:NSTimer?
    var currentBgTaskId : UIBackgroundTaskIdentifier?
    var lastLocationDate : NSDate = NSDate()

    private override init(){
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
        locationManager.activityType = .Other;
        locationManager.distanceFilter = kCLDistanceFilterNone;
        if #available(iOS 9, *){
            locationManager.allowsBackgroundLocationUpdates = true
        }

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.applicationEnterBackground), name: UIApplicationDidEnterBackgroundNotification, object: nil)
    }

    func applicationEnterBackground(){
        FileLogger.log("applicationEnterBackground")
        start()
    }

    func start(){
        if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedAlways){
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        } else {
                locationManager.requestAlwaysAuthorization()
        }
    }
    func restart (){
        timer?.invalidate()
        timer = nil
        start()
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        switch status {
        case CLAuthorizationStatus.Restricted:
            //log("Restricted Access to location")
        case CLAuthorizationStatus.Denied:
            //log("User denied access to location")
        case CLAuthorizationStatus.NotDetermined:
            //log("Status not determined")
        default:
            //log("startUpdatintLocation")
            if #available(iOS 9, *){
                locationManager.requestLocation()
            } else {
                locationManager.startUpdatingLocation()
            }
        }
    }
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        if(timer==nil){
            // The locations array is sorted in chronologically ascending order, so the
            // last element is the most recent
            guard let location = locations.last else {return}

            beginNewBackgroundTask()
            locationManager.stopUpdatingLocation()
            let now = NSDate()
            if(isItTime(now)){
                //TODO: Every n minutes do whatever you want with the new location. Like for example sendLocationToServer(location, now:now)
            }
        }
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        CrashReporter.recordError(error)

        beginNewBackgroundTask()
        locationManager.stopUpdatingLocation()
    }

    func isItTime(now:NSDate) -> Bool {
        let timePast = now.timeIntervalSinceDate(lastLocationDate)
        let intervalExceeded = Int(timePast) > BackgroundLocationManager.UPDATE_SERVER_INTERVAL
        return intervalExceeded;
    }

    func sendLocationToServer(location:CLLocation, now:NSDate){
        //TODO
    }

    func beginNewBackgroundTask(){
        var previousTaskId = currentBgTaskId;
        currentBgTaskId = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
            FileLogger.log("task expired: ")
        })
        if let taskId = previousTaskId{
            UIApplication.sharedApplication().endBackgroundTask(taskId)
            previousTaskId = UIBackgroundTaskInvalid
        }

        timer = NSTimer.scheduledTimerWithTimeInterval(BackgroundLocationManager.BACKGROUND_TIMER, target: self, selector: #selector(self.restart),userInfo: nil, repeats: false)
    }
}

Я починаю відстеження в AppDelegate так:

BackgroundLocationManager.instance.start()

Дякую. Наш проект повинен відстежувати місцезнаходження користувача + надсилати події PubNub у фоновому режимі, і ваше рішення працює дуже добре.
user921509

Привіт Хмітков, куди я можу зателефонувати метод sendLocationToServer для надсилання місцезнаходження користувача на сервер
AmanGupta007

@ AmanGupta007 Ви можете зателефонувати sendLocationToServer у func locationManager (менеджер: didUpdateLocations :). Зауважте // коментар TODO в коді.
хмітков

@hmitkov Чи можна запускати та зупиняти Служби локації за допомогою цього, коли програма знаходиться у фоновому режимі? Наприклад, запустіть службу локації з натискання сповіщення, захопіть трохи lat / long, надішліть на веб-сервіс, а потім зупиніть оновлення місця. Робіть це щоразу, коли "тіст-доступний" = 1 включений у тіло push.
DookieMan

1
Я спробував цей код, але здається, він не працює на iOS11? (Я не перевіряв його на будь-яких інших версіях.)
Том

6

На жаль, всі ваші припущення здаються правильними, і я не думаю, що існує спосіб це зробити. З метою економії заряду акумулятора служби локації iPhone засновані на русі. Якщо телефон сидить на одному місці, він не помітний для локальних служб.

CLLocationManagerВикликатиме тільки locationManager:didUpdateToLocation:fromLocation:коли телефон отримує оновлення розташування, яке відбувається тільки тоді , коли один з трьох служб визначення місцеположення (вишки стільникового зв'язку , GPS, WiFi) сприймає зміна.

Ще кілька речей, які можуть допомогти інформувати подальші рішення:

  • Запуск та зупинка послуг викликає didUpdateToLocationвиклик методу делегата, але newLocationможе мати стару позначку часу.

  • Моніторинг регіону може допомогти

  • Працюючи у фоновому режимі, пам’ятайте, що може бути важко отримати «повну» підтримку LocationServices, затверджену компанією Apple. З того, що я бачив, вони спеціально розроблені startMonitoringSignificantLocationChangesяк альтернатива малої потужності для додатків, яким потрібна підтримка фонового розташування, і наполегливо заохочують розробників використовувати це, якщо додаток абсолютно не потребує цього.

Щасти!

ОНОВЛЕННЯ: Наразі ці думки застаріли. Виглядає так, ніби люди мають успіх у відповіді @wjans, вище.


1
У AppStore є додатки (наприклад, "Мій локус"), які дозволяють отримати оновлення місцезнаходження у фоновому режимі. Вони не підтримують службу локації активною, але вона стає активованою незабаром відповідно до визначеного інтервалу. Як вони це роблять?
wjans

2
У ситуації, яку ви описуєте, програма, швидше за все, використовує підхід startMonitoringSignificantLocationChanges. Тут телефон тимчасово "прокидається", коли отримує оновлення місцеположення, але немає інтервалу, який можна встановити на "пінг" цієї послуги у фоновому режимі. Коли телефон рухається (або переходить з мобільного на GPS або на Wi-Fi), він запускає оновлення. Лекція Stanford iTunes U на цю тему була дуже корисною для мене - сподіваємось, вона може допомогти вам знайти вирішення: itunes.apple.com/us/itunes-u/iphone-application-development/…
Chazbot

2
Thx для вказівника. Однак я все ще не розумію, що робить додаток. Навіть якщо мій телефон стоїть за моїм столом, і зовсім не рухається, я бачу, що служба локації спрацьовує кожні 10 хвилин (саме). Якщо я правильно розумію, startMonitoringSignificantLocationChangesв такому разі не оновлюватимуться.
wjans

1
@wjans, як витрачається акумулятор у вашому телефоні, чи помітили ви, що він швидко стікає, можливо, через додаток Mylocus?
користувач836026

6

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

Просто скористайтеся методом " enableDeferredLocationUpdatesUntilTraveled: timeout ", дотримуючись док. Apple.

Що я зробив:

Обов’язково: Зареєструйте фоновий режим для оновлення Місцеположення.

1. Створіть LocationMangerі startUpdatingLocation, як accuracyі 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. Додаток оновлюєтьсяLocations як звичайний із 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кожного разу, коли програма перемикається між фоновим / фоновим режимом.


@ Всім, яке рішення цього або вище поясненого рішення wjans я повинен вважати за краще заощадити акумулятор пристрою, або те чи інше вплине на те саме?
Яш

2
Я спробував обидва згадані вами рішення, і побачив, що той, хто користується @ wjans, трохи більше економить акумулятор. Але після появи iOS 8, схоже, це рішення вже не працює належним чином. Для більш детальної інформації: більшість часу програма не може довго жити у фоновому режимі.
samthui7

@ samthui7 Чому ви встановлюєте паузиLocationUpdatesAutomatically = false?
CedricSoubrie

Вимога мого додатка полягає у тому, щоб кожні 10 секунд надсилати UserLocation, при цьому pausesLocationUpdatesAutomatically = trueповідомляє менеджеру локації призупиняти оновлення, якщо, мабуть, не змінюється місцезнаходження (документ Apple ). У всякому разі, я ще не явно перевіряв випадок location manager pauses updates: D.
samthui7

@CedricSoubrie: налаштування pausesLocationUpdatesAutomatically = trueпризводить до того, що мій додаток зупиняє оновлення місцезнаходження.
samthui7

5
if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    [self.locationManager setAllowsBackgroundLocationUpdates:YES];
}

Це потрібно для відстеження розташування фону, починаючи з iOS 9.


1
Це врятувало мені день! використовуючи ціль розгортання iOS8, з пристроєм iOS9
Яро

4

Я використав метод xs2bush отримання інтервалу (за допомогою timeIntervalSinceDate) і трохи розширив його. Я хотів переконатися, що я отримую потрібну точність, яка мені потрібна, а також, що я не розряджаю батарею, утримуючи радіо gps більше, ніж потрібно.

Я постійно зберігаю місцеположення з такими налаштуваннями:

locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
locationManager.distanceFilter = 5;

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

locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.distanceFilter = 0;

знайдіть своє місцезнаходження, а потім, коли я знайду місце, я знову повертаю точність, щоб мінімізувати витрату акумулятора. Я написав повний робочий зразок цього, а також написав джерело коду на стороні сервера, щоб зібрати дані про місцеположення, зберігати їх у базі даних та дозволяти користувачам переглядати дані gps в режимі реального часу або отримувати та переглядати раніше збережені маршрути. У мене є клієнти для iOS, android, windows phone та java. Усі клієнти написані на самому справі, і всі вони працюють належним чином у фоновому режимі. Проект має ліцензію MIT.

Проект iOS орієнтований на iOS 6, використовуючи базовий SDK iOS 7. Код ви можете отримати тут .

Будь-ласка, подайте проблему на github, якщо у вас є проблеми. Дякую.


Я спробував ваше рішення, але не працює ... коли додаток переходить у фоновий режим, навіть після підвищення точності додаток не отримує назву методу didUpdateToLocation у моєму випадку
Hardik Darji

@HardikDarji важливе питання. Ви рухаєтесь? Якщо ні, оновлення місцеположення можуть припинитися. Спробуйте винести телефон на прогулянку чи диск, і подивіться, чи це це виправить.
нікфокс

Дякуємо за вашу швидку відповідь. Але я хочу оновлювати місце розташування кожні 2 хв., Не піклуючись про те, щоб мій телефон рухався чи ні. У цьому сценарії метод didUpdateToLocation не викликається. Я шукаю тут: Як мені оновлювати місцезнаходження кожні n хвилин !!!
Hardik Darji

спробуйте встановити timeIntervalInSeconds на 120 та відменте цей рядок: locationManager.pausesLocationUpdatesAutomatically = НІ;
нікфокс

1
чи рішення, яке ви опублікували у вищенаведеному коментарі, вбиває ресурс акумулятора, або ви все ще робите певну оптимізацію для цього?
samfr

2

Здається, що StopUpddLocation - це те, що запускає фоновий таймер сторожового дога, тому я замінив його на didUpdateLocation на:

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

який, як видається, ефективно вимикає GPS. Селектор для фонового NSTimer потім стає:

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

Все, що я роблю, - це періодично переключати точність, щоб отримати координату високої точності кожні кілька хвилин, а оскільки розташування менеджера не зупинено, backgroundTimeRemaining залишається на максимальному значенні. Це зменшило споживання акумулятора з ~ 10% на годину (з постійним kCLLocationAccuracyBest у фоновому режимі) до ~ 2% на годину на моєму пристрої


2

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

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

Репозиторій також містить приклад програми, написаної на Swift 3.


у вас є щось подібне в об'єктивному c?
Моксарт

1

У iOS 9 та watchOS 2.0 на CLLocationManager є новий метод, який дозволяє запитувати поточне місцезнаходження: CLLocationManager: requestLocation (). Це завершується негайно, а потім повертає місце делегату CLLocationManager.

Ви можете використовувати NSTimer, щоб запитувати місцезнаходження щохвилини за допомогою цього методу зараз, і не потрібно працювати з методами startUpdatingLocation та stopUpdatingLocation.

Однак якщо ви хочете захопити місця на основі зміни X метрів від останнього місця, просто встановіть властивість відстаніFilter CLLocationManger та X виклику startUpdatingLocation ().


-1

Додано рішення Swift, засноване на:

Визначте App registers for location updatesв info.plist

Продовжуйте постійно керувати locationManager

Перемикайтеся kCLLocationAccuracyміж BestForNavigation(на 5 секунд, щоб отримати місце розташування) та ThreeKilometersна інший період очікування, щоб уникнути розрядки акумулятора

Цей приклад оновлює розташування кожні 1 хв на передньому плані та кожні 15 хв у фоновому режимі.

Приклад прекрасно працює з Xcode 6 Beta 6, що працює на пристрої iOS 7.

У програмі "Делегат додатків" (mapView - необов'язковий вказівник на контролер mapView)

func applicationDidBecomeActive(application: UIApplication!) {
    if appLaunched! == false { // Reference to mapView used to limit one location update per timer cycle
        appLaunched = true
        var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        var window = appDelegate.window
        var tabBar = window?.rootViewController as UITabBarController
        var navCon = tabBar.viewControllers[0] as UINavigationController
        mapView = navCon.topViewController as? MapViewController
    }
    self.startInitialPeriodWithTimeInterval(60.0)
}

func applicationDidEnterBackground(application: UIApplication!) {
    self.startInitialPeriodWithTimeInterval(15 * 60.0)
}

func startInitialPeriodWithTimeInterval(timeInterval: NSTimeInterval) {
    timer?.invalidate() // reset timer
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getFirstLocationUpdate:"), userInfo: timeInterval, repeats: false)
}

func getFirstLocationUpdate(sender: NSTimer) {
    let timeInterval = sender.userInfo as Double
    timer?.invalidate()
    mapView?.canReportLocation = true
    timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector("waitForTimer:"), userInfo: timeInterval, repeats: true)
}

func waitForTimer(sender: NSTimer) {
    let time = sender.userInfo as Double
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    finalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getLocationUpdate"), userInfo: nil, repeats: false)
}

func getLocationUpdate() {
    finalTimer?.invalidate()
    mapView?.canReportLocation = true
}

У mapView (locationManager вказує на об'єкт у AppDelegate)

override func viewDidLoad() {
    super.viewDidLoad()
    var appDelegate = UIApplication.sharedApplication().delegate! as AppDelegate
    locationManager = appDelegate.locationManager!
    locationManager.delegate = self
    canReportLocation = true
}

  func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        if canReportLocation! {
            canReportLocation = false
            locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        } else {
            //println("Ignore location update")
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.