2015-08-06 6 views
0

Я пытаюсь создать приложение, которое может запускаться iBeacon для пробуждения (от момента его увольнения/приостановления/завершения) для записи второй по времени информации GPS. Затем запись GPS должна прекратиться, когда телефон выходит за пределы диапазона маяка. Я успешно получил приложение, чтобы узнать методы didEnterRegion и didExitRegion, когда он входит и выходит за пределы iBeacon. В методе didEnterRegion я хочу в основном сказать что-то вроде [locationManager startUpdatingLocation], чтобы я мог начать отслеживать местоположение пользователя. Однако, когда я пытаюсь добавить эту строку кода, обновления местоположения останавливаются примерно через 10 секунд.Обновление фонового местоположения iOS 8, вызванное iBeacon

Позже я нашел article об обновлении местоположения фона, которые пришли с этим Github project. Я добавил файлы BackgroundTaskManager, LocationShareModel и LocationTracker в свой проект. В принципе, идея этого решения заключается в том, чтобы постоянно перезапускать диспетчер местоположений, чтобы у него не было возможности для истечения фоновой задачи и прекращения отправки обновлений. Однако даже с этим решением я получаю обновления местоположения чуть более 3 минут.

У меня есть опции «Обновления местоположения» и «Использовать аксессуары Bluetooth LE». «Фоновая выборка» (Refresh Background Refresh) не включена в соответствии с этой цитатой от Apple: «В iOS 8 и более поздних версиях отключить настройку обновления фонового приложения для текущего приложения или для всех приложений не помешает доставка местоположения события в фоновом режиме ". Мое приложение запрашивает разрешение «Всегда» для обновления местоположения.

Я не могу понять, как решить эту проблему, несмотря на обзор, казалось бы, бесконечных статей и учебников StackOverflow. Я тестирую его на iPhone 5S под управлением iOS 8.3.0. Любое понимание было бы оценено. См. Фрагменты кода ниже.

В AppDelegate.m:

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { 
    if ([region isKindOfClass:[CLBeaconRegion class]]) { 
     UILocalNotification *notification = [[UILocalNotification alloc] init]; 
     notification.alertBody = @"Start recording trip"; 
     notification.soundName = @"Default"; 
     [[UIApplication sharedApplication] presentLocalNotificationNow:notification]; 

     self.recording = YES; 
     [self startAutoTrip]; 
    } 
} 

- (void) startAutoTrip { 
    self.locationTracker = [[LocationTracker alloc]init]; 
    [self.locationTracker startLocationTracking]; 
} 

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { 
    if ([region isKindOfClass:[CLBeaconRegion class]]) { 
     UILocalNotification *notification = [[UILocalNotification alloc] init]; 
     notification.alertBody = @"Stop recording trip"; 
     notification.soundName = @"Default"; 
     [[UIApplication sharedApplication] presentLocalNotificationNow:notification]; 

     [self stopAutoTrip]; 
     self.recording = NO; 
    } 
} 

- (void)stopAutoTrip { 

    // stop recording the locations 
    CLSLog(@"Trying to stop location updates"); 
    [self.locationTracker stopLocationTracking:self.managedObjectContext]; 
    CLSLog(@"Stop location updates"); 
} 

В LocationTracker.m (из учебника цитированной выше, изменить интервалы времени 60 сек и 10 сек до 5 сек и 2 сек). В основном это методы startLocationTracking, didUpdateLocations и stopLocationTracking.

- (void)startLocationTracking { 
NSLog(@"startLocationTracking"); 

    if ([CLLocationManager locationServicesEnabled] == NO) { 
     NSLog(@"locationServicesEnabled false"); 
     UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:@"Location Services Disabled" message:@"You currently have all location services for this device disabled" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 
     [servicesDisabledAlert show]; 
    } else { 
     CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus]; 

     if(authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted){ 
      NSLog(@"authorizationStatus failed"); 
     } else { 
      NSLog(@"authorizationStatus authorized"); 
      CLLocationManager *locationManager = [LocationTracker sharedLocationManager]; 
      locationManager.delegate = self; 
      locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
      locationManager.distanceFilter = 10; //meters 
      locationManager.activityType = CLActivityTypeAutomotiveNavigation; 
      locationManager.pausesLocationUpdatesAutomatically = NO; 

      if(IS_OS_8_OR_LATER) { 
       [locationManager requestAlwaysAuthorization]; 
      } 
      [locationManager startUpdatingLocation]; 
     } 
    } 
} 

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{ 

    NSLog(@"locationManager didUpdateLocations"); 

    for(int i=0;i<locations.count;i++){ 
     CLLocation * newLocation = [locations objectAtIndex:i]; 

     NSDate *eventDate = newLocation.timestamp; 

     NSTimeInterval howRecent = [eventDate timeIntervalSinceNow]; 

     if (fabs(howRecent) < 10.0 && newLocation.horizontalAccuracy < 20 && locations.count > 0) { 


      CLLocationCoordinate2D theLocation = newLocation.coordinate; 
      CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy; 

      self.myLastLocation = theLocation; 
      self.myLastLocationAccuracy= theAccuracy; 

      CLLocationCoordinate2D coords[2]; 
      coords[0] = ((CLLocation *)locations.lastObject).coordinate; 
      coords[1] = newLocation.coordinate; 

      [self.shareModel.myLocationArray addObject:newLocation]; 

     } 
    } 

    //If the timer still valid, return it (Will not run the code below) 
    if (self.shareModel.timer) { 
     return; 
    } 

    self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager]; 
    [self.shareModel.bgTask beginNewBackgroundTask]; 

    //Restart the locationMaanger after 1 minute (5 sec) 
    self.shareModel.timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self 
                 selector:@selector(restartLocationUpdates) 
                 userInfo:nil 
                 repeats:NO]; 

    //Will only stop the locationManager after 10 seconds, so that we can get some accurate locations 
    //The location manager will only operate for 10 seconds to save battery 
    // 2 sec 
    if (self.shareModel.delay10Seconds) { 
     [self.shareModel.delay10Seconds invalidate]; 
     self.shareModel.delay10Seconds = nil; 
    } 

    self.shareModel.delay10Seconds = [NSTimer scheduledTimerWithTimeInterval:2 target:self 
               selector:@selector(stopLocationDelayBy10Seconds) 
               userInfo:nil 
               repeats:NO]; 

} 

- (void) restartLocationUpdates 
{ 
    NSLog(@"restartLocationUpdates"); 

    if (self.shareModel.timer) { 
     [self.shareModel.timer invalidate]; 
     self.shareModel.timer = nil; 
    } 

    CLLocationManager *locationManager = [LocationTracker sharedLocationManager]; 
    locationManager.delegate = self; 
    locationManager.desiredAccuracy = kCLLocationAccuracyBest; 
    locationManager.distanceFilter = 10; //meters 
    locationManager.activityType = CLActivityTypeAutomotiveNavigation; 
    locationManager.pausesLocationUpdatesAutomatically = NO; 

    if(IS_OS_8_OR_LATER) { 
     [locationManager requestAlwaysAuthorization]; 
    } 
    [locationManager startUpdatingLocation]; 
} 

- (void)stopLocationTracking:(NSManagedObjectContext *)managedObjectContext  { 
    NSLog(@"stopLocationTracking"); 
    CLSLog(@"stopLocationTracking"); 

    CLSLog(@"set managedObjectContext %@", managedObjectContext); 
    self.managedObjectContext = managedObjectContext; 

    if (self.shareModel.timer) { 
     [self.shareModel.timer invalidate]; 
     self.shareModel.timer = nil; 
    } 

    CLLocationManager *locationManager = [LocationTracker sharedLocationManager]; 
    [locationManager stopUpdatingLocation]; 

    [self saveRun]; 
    [self sendRun]; 
} 
+0

_ «Я пытаюсь создать приложение, которое может быть запущено iBeacon, чтобы проснуться (от того, чтобы быть убитым/приостановлено/завершено)». К сожалению, это невозможно в iOS. Приложение должно быть запущено на переднем плане/фон, чтобы он мог общаться с ibeacons. –

ответ

1

Спасибо всем за ваши ответы. Можно разбудить ваше приложение от того, чтобы его убили/приостановили/прекратили с помощью iBeacons, вопреки тому, что сказал Øyvind Hauge. И, к сожалению, добавление режима фонового местоположения в ваш plist не позволяет устанавливать неопределенные обновления местоположения, как предлагали другие; Я только смог получить 3 минуты исполнения, используя этот метод.

Я действительно нашел решение своего вопроса в этом StackOverflow article. Решение состоит в том, чтобы добавить всего несколько строк кода в ваш делегат приложения - вам нужно запустить другой менеджер местоположений, который отслеживает значительные обновления местоположения. Вот строки кода, которые я добавил к моему didFinishLaunchingWithOptions метод в моем файле AppDelegate.m после объявления anotherLocationManager как свойство ...

self.anotherLocationManager = [[CLLocationManager alloc] init]; 
self.anotherLocationManager.delegate = self; 
[self.anotherLocationManager startMonitoringSignificantLocationChanges]; 

Я никогда ничего не делать еще с помощью этого менеджера местоположения, я просто оставить его бессрочно работающий в фоновом режиме, и по какой-либо причине это позволяет неопределенные обновления местоположения от обычного вызова до [locationManager startUpdatingLocation]. У меня больше нет обновлений местоположения, которые таинственно останавливаются через 3 минуты. Кажется очень странным, что это было решение, но было довольно просто реализовать, и, надеюсь, это поможет другим, кто имеет дело с одной и той же проблемой.

+0

Вау, работает ли это даже после того, как приложение приостановлено и перезапущено из-за обновления iBeacon? Будут ли начальные обновления местоположения, а затем просто заставить приложение работать бесконечно? – user3334059

+0

Это работает после того, как приложение было приостановлено или даже убито пользователем. IBeacon запускает приложение для перезапуска, а затем запускаю диспетчер местоположений. Как я уже упоминал в своем вопросе, у меня есть менеджер фоновых задач, который перезапускает диспетчер местоположений по таймеру, чтобы приложение запускалось неограниченное время. Таким образом, крупномасштабный обзор - это мониторинг iBeacon, который запускает диспетчер местоположений (который сам управляется фоновым менеджером задач), но эта настройка не работает без мониторинга значительных изменений местоположения, как видно из моего ответа. – Carielle

0

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

Вы можете увидеть пример того, как это сделать в this blog post о расширении маяка на фоне. В то время как в сообщении в блоге упоминается, что это ограничено 3 минутами, когда вы добавляете режим фонового изображения местоположения на ваш plist, это ограничение времени уходит.

Поймите, что вы не можете получить одобрение AppStore для использования этого режима фона, если Apple не оценит ваше обоснование для этого.

+0

Это тоже было мое понимание, но, судя по ответам Суманта, вам действительно нужно начать обновление местоположения на переднем плане, чтобы они работали бесконечно, даже если включен режим фонового местоположения. Дело Кариэль, похоже, подтверждает это. Интересно! – heypiotr

0

Итак, в iOS обновления местоположения будут работать в фоновом режиме на неопределенный срок ТОЛЬКО, если - 1. Вы начали обновление местоположения на переднем плане И 2. Вы добавили фоновое местоположение в свой слой.

В вашем случае ОС пробуждает вас в фоновом режиме, и, как вы сказали правильно, вы получаете 10 секунд времени выполнения, прежде чем ОС приостанавливает ваше приложение. Обходной путь для этого - это, в основном, запуск фоновой задачи, как вы это сделали, чтобы получить дополнительные 180 секунд времени выполнения (это число может меняться в зависимости от версии ОС).

Чтобы понять вашу проблему в глубину, вам нужно знать, что есть только определенные события (например, geofence/ibeacon/значительное обновление местоположения), которые пробудят ваше приложение в фоновом режиме, позвольте нам назвать их событиями «пробуждения». Как только произойдет какое-либо из этих событий, у вас будет максимум 180 секунд времени выполнения фона (с использованием фоновой задачи), после которого ваше приложение будет приостановлено, если ни одно из этих событий не будет запущено снова, после чего вам необходимо перезапустить фоновую задачу. Я не уверен, как ваше приложение работает точно, но если вы можете обеспечить, чтобы вы продолжали получать эти события «пробуждения» из ОС на время, для которого вам нужны обновления местоположения, вы можете в значительной степени сохранить свое приложение в фоновом режиме.

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

Смежные вопросы