Приложение, убитое ОС в ios7 через несколько секунд

Я создаю навигационное приложение для iOS 7, для него я беру данные о местоположении пользователей, используя каркас CoreLocation,

Требование приложения состоит в том, чтобы начать получать местоположение пользователей в фоновом режиме в определенное время, для этого я реализовал Silent Pushnotification с didReceiveRemoteNotification fetchCompletionHandler: метод,

Я успешно реализовал это, используя Silent Pushnotification и его вызов startUpdatingLocation и я могу получить данные о местоположении в методе делегата:

Используя эту полезную нагрузку:

{"aps" : {"content-available" : 1},"SilentPush" : "4"}

Я включил location & remote notification для фонового режима:

введите описание изображения здесь

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
     __block UIBackgroundTaskIdentifier bgTask =0;
    UIApplication  *app = [UIApplication sharedApplication];
     bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [self.locationManager startUpdatingLocation];

 }];

didUpdateLocations

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
        lastLoc=[locations lastObject];
        [logFile addLocObject:[NSString stringWithFormat:@"Loc: %@",lastLoc]];
}

Но проблема в следующем:

Через несколько секунд метод делегата класса местоположения останавливается, и он не будет отправлять какие-либо данные, если устройство перемещается. И когда я обращаюсь к приложению на переднем плане, оно будет вызываться методом didFinishLaunhing, поэтому я предполагаю, что os убьет приложение, даже если Location обновляется, в устройство Diagnostics & usage я получаю следующий отчет о сбое:

Application Specific Information:
MockUpApp2[390] has active assertions beyond permitted time: 
{(
    <BKProcessAssertion: 0x145ac790> identifier: Called by MockUpApp2, from -[AppDelegate application:didReceiveRemoteNotification:fetchCompletionHandler:] process: MockUpApp2[390] permittedBackgroundDuration: 40.000000 reason: finishTaskAfterBackgroundContentFetching owner pid:390 preventSuspend  preventIdleSleep  preventSuspendOnSleep 
)}

Вчера я задал этот вопрос, теперь я могу запустить диспетчер местоположения в фоновом режиме через push-уведомление,

Поэтому, пожалуйста, кто-нибудь может дать решение этой проблемы.

Спасибо.

Примечание. Если я запускаю приложение в режиме отладки, это означает, что я запускаю приложение через XCode, не останавливая его или не отключая, приложение будет запущено. В этом случае приложение не будет остановлено операционной системой.

РЕДАКТИРОВАТЬ 1

Согласно @Stephen Darlington Ответ, если я удаляю все backgroundfatcher как,

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
        [self.locationManager startUpdatingLocation];

}

Теперь приложение не будет звонить didUpdateLocations даже однажды,:(

Что я должен написать в этом методе?

Edit 2

Согласно Apple Doc сказать:

If your app is suspended or not running, the system wakes up or launches your app and puts it into the background running state before calling the method.

Так, приложение должно работать в фоновом режиме, пока я включил режим локации фона,?

3 ответа

Я не думаю, что фоновая обработка на iOS работает так, как вы ожидаете. Это не так, как на Mac или Windows, где вы можете работать бесконечно в фоновом режиме (даже с изменениями в iOS 7).

Итак, чтобы прямо ответить на ваш вопрос: вы запускаете задачу, которая может продолжать выполняться в фоновом режиме. Есть два "но".

  1. Блок кода является обработчиком срока действия. Это не тот код, который вы хотите запустить в фоновом режиме. Этот код будет выполнен незадолго до истечения срока действия фоновой задачи (поэтому в вашем случае он выполняется незадолго до того, как ваше приложение будет убито). Apple на самом деле не документирует, как долго это длится, но похоже, что вы получаете около 40 секунд. Код, который вы хотите запустить в фоновом режиме, это строки после beginBackgroundTaskWithExpirationHandler, Когда вы закончите, вы говорите endBackgroundTask:
  2. Во-вторых, обновления местоположения работают в фоновом режиме без всех beginBackgroundTaskWithExpirationHandler: вещи. Методы делегата будут вызваны, даже если приложение находится в фоновом режиме.

Подводя итог: удалить beginBackgroundTaskWithExpirationHandler: заявление. Вам нужно только добавить startUpdatingLocation:,

Некоторое время назад я довольно много работал с CLLocation, и у меня есть некоторые комментарии и предложения, хотя, возможно, и не полное решение.

Я вижу, что вы помещаете свою реакцию на молчаливое уведомление в beginBackgroundTaskWithExpirationHandler блок. Я не думаю, что это правильно. Однако я считаю, что вы должны использовать этот шаблон backgroundTask в методах CLLocationDelegate.

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

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

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

Я бы подумал о схеме примерно так:

  • происходит значительное изменение местоположения. Проверьте скорость / активность. Вы можете сделать это без включенных фоновых сервисов.
  • если вождение просто сохранить место. Вы будете часто меняться во время вождения, поэтому вы будете отставать только на несколько минут.
  • не за рулем или в режиме ожидания, создайте область разумного размера в зависимости от ваших требований к точности. Пока устройство находится в этом регионе, вы можете использовать текущее сохраненное недавнее местоположение. Это будет происходить большую часть времени в течение 24-часового периода, поэтому вы в целом сильно влияете на батарею.

Мне кажется, вы идете на это с неправильной точки зрения. Логично думать, что вы отправите сообщение, а затем ответите, заставив CL сделать что-то. Это не так, как сервис предназначен. По сути, вы настроили диспетчер местоположений и отпустили его.

Вам может быть полезно посмотреть на некоторые шаблоны, используемые в репозитории, который у меня есть на GitHub.

TTLocationHandler

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

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

События вашего местоположения обрабатываются по мере их поступления, фона или нет.

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

Правка - правильное использование блока обработчика выдоха

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    __block UIBackgroundTaskIdentifier bgTask =0;
    UIApplication  *app = [UIApplication sharedApplication];
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
          // Do something here. Perhaps you'll log the error or respond with some fall back
          // This block will only be run if the handler expires without having been ended
          // In that case, you need to end it now or you will crash
          If (bgTask != UIBackgroundTaskInvalid) {
              [app endBackgroundTask:bgTask];
              [bgTask = UIBackgroundTaskInvalid];
          }

      }];

      // This is where the code you intend to run in the background starts

     [self.locationManager startUpdatingLocation];

     // Now tell the system you are finished, ending background task normally before exit

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

Метод beginBackgroundTaskWithExpirationHandler: дать приложению время для его вещей, но это не значит, что задача может выполняться вечно.
Ваше приложение убито, потому что у вас явно заканчивается время, как указано в журнале сбоев.
Вы должны получить тот же результат, просто запрашивая обновление местоположения без включения в обработчик срока действия.
Я помню, что в старых системах мне удавалось нечто подобное, вызывая обновления местоположения в фоновом режиме с помощью мониторинга региона.
[РЕДАКТИРОВАТЬ]
Чтобы получить лучший результат при игре с локацией, я предлагаю вам использовать реальные устройства и, возможно, вы.

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