Вызывать текущие координаты каждые несколько секунд без NSTimer

Я знаю, как сделать это с NSTimer, но я не хочу получать текущие координаты iPhone без таймера каждые несколько секунд. Я не могу использовать таймер, потому что я получаю координаты, пока приложение находится в фоновом режиме.
Я пытался что-то, но это звонит каждую секунду, а не каждые 10 секунд, как я не хочу.
Что я здесь не так делаю?

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

    NSDate* eventDate = loc.timestamp;
    NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
    if (howRecent < 10)
    {
        CLLocation* location = [locations lastObject];

        double lat = location.coordinate.latitude;
        double lng = location.coordinate.longitude;
        NSLog(@"lat:%f lng:%f", lat, lng);

Сначала попробуйте выполнять фоновое задание каждые 10 секунд, но не знаете, где установить время, если я что-то сделаю прямо здесь:

- (void)applicationDidEnterBackground:(UIApplication *)application
{

    backgroundTask = [application beginBackgroundTaskWithExpirationHandler: ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            if (backgroundTask != UIBackgroundTaskInvalid)
            {
                [application endBackgroundTask:backgroundTask];
                backgroundTask = UIBackgroundTaskInvalid;
            }
        });
    }];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        _pendingLocationsTimer = [NSTimer timerWithTimeInterval:_pendingLocationsTimerDuration
                                                         target:self
                                                       selector:@selector(_acceptBestAvailableLocation:)
                                                       userInfo:nil repeats:NO];


        NSRunLoop *loop = [NSRunLoop currentRunLoop];
        [loop addTimer:_pendingLocationsTimer forMode:NSRunLoopCommonModes];
        [loop run];

        dispatch_async(dispatch_get_main_queue(), ^{
            if (backgroundTask != UIBackgroundTaskInvalid)
            {
                // if you don't call endBackgroundTask, the OS will exit your app.
                [application endBackgroundTask:backgroundTask];
                backgroundTask = UIBackgroundTaskInvalid;
            }
        });
    });

}

2 ответа

Решение

У меня были проблемы с таймерами в фоновом состоянии. Нет гарантии, что у вас будет активный цикл выполнения, и поэтому таймер никогда не сработает, пока вы не вернетесь на передний план. Что я сделал, это:

 NSRunLoop *loop = [NSRunLoop currentRunLoop];
 [loop addTimer:myTimer forMode:NSRunLoopCommonModes];
 [loop run];

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

Вы также должны включить фоновый режим для служб определения местоположения, включенный в ваш info.pList по умолчанию, если вы хотите постоянно отслеживать местоположение. Вам это не нужно, если вы просто используете значимые атрибуты местоположения или геозону.

Мой таймер работает и работает так, как задумано. До этого он не работал надежно.

Кроме того, вы можете быть лучше без таймера. Используя ваш код выше, просто измените свойства вашего менеджера местоположения. Требуемая точность и дистанционный фильтр - это свойства, которые снижают частоту отправки менеджером уведомления о новом местоположении.

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

http://github.com/dsdavids/TTLocationHandler

Я не думаю, что таймер является ключом. Если вы удалите класс TTLocationHandler в своем проекте, посмотрите, как класс LMViewController реагирует на обработчик.

NSNotificationCenter *defaultNotificatoinCenter = [NSNotificationCenter defaultCenter];
[defaultNotificatoinCenter addObserver:self selector:@selector(handleLocationUpdate) name:LocationHandlerDidUpdateLocation object:nil];

устанавливает контроллер в качестве наблюдателя. Всякий раз, когда обработчик определяет, что получено новое или более точное местоположение, вызывается handleLocationUpdate.

LocationManager, когда вызывается startUpdating, будет отправлять новые местоположения, много раз в секунду вначале и при перемещении, пока устройство не станет неподвижным и не будет достигнута точность. TTLocationHandler фильтрует эти события и отправляет уведомление только по мере необходимости на основе конфигурации.

Обратите внимание, что в -(id)init, _recencyThreshold установлен на 60. Поэтому мы сохраняем или отображаем пин-код каждые 60 секунд. Если вы хотите меньший интервал, измените это.

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

Я добавил свойство для настройки для непрерывных фоновых обновлений без зарядки.

Пример использования TTLocationHandler

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

Посмотрите на LMAppDelegate.m

    self.pinTracker.uploadInterval = 30.00;

измените это на любой интервал между загрузками на сервер.

    self.sharedLocationHandler.recencyThreshold = 5.0;

Измените значение 5.0 на минимальное время между сохраненными местоположениями. Он не будет точным, будет зависеть от скорости перемещения и мощности сигнала, но, по сути, через x секунд он будет считать сохраненное местоположение устаревшим и попытаться получить другое точное местоположение.

Теперь посмотрите на файл LMPinTracker.m
Поместите код хранения данных сразу после этих двух строк:

    // Store the location into your sqlite database here
    NSLog(@"Received location info: %@ \n Ready for store to database",lastKnowLocationInfo);

Введите код загрузки через Интернет прямо после этого комментария:

    // Do your upload to web operations here

Комментарии

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

В основном, когда пользователь путешествует, обновления будут выполняться непрерывно с установленным вами интервалом.
Когда пользователь стоит на месте, не будет никаких обновлений и значительных действий.
Если нет новых действий, вы также не будете загружать их в Интернет.
Регистрация и обновление возобновляются, когда пользователь снова начинает движение.

Если бы я был вами, желая уточнить его, я бы подумал об установке региона и времени для проверки. Если пользователь остается неподвижным в течение некоторого времени, переключитесь только на значительное время обновления. Затем вы выключите службы определения местоположения, но будете снова включены, когда пользователь снова начнет работу.

Я думаю, что вы можете сделать это в фоновом режиме с таймером:

[NSTimer scheduledTimerWithTimeInterval:kAlertInterval target:self selector:@selector(checkLoc:) userInfo:nil repeats:YES];

И в вашем методе:

- (void)checkLoc:(NSTimer *)timer {
    //...get your location and...
     if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
        //Do your background stuff
    }
}

У меня этот код работает в некоторых приложениях. Пожалуйста, скажите мне, если это работает для вас.

Другие вопросы по тегам