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

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

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

Я успешно реализовал это, используя тихое push-уведомление и вызов 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», поэтому я предполагаю, что ОС убьет приложение, даже если местоположение обновляется, в Устройство 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, если я удалю все фоновые изображения, например,

- (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.

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


person Community    schedule 07.02.2014    source источник
comment
@trojanfoe: я упоминал об этом в вопросе, и это другая ветка.   -  person    schedule 07.02.2014
comment
Что делает ваш код, когда получает новое местоположение?   -  person Wain    schedule 07.02.2014
comment
@Wain: сейчас я просто храню объект CLLocation в текстовом файле.   -  person    schedule 07.02.2014
comment
у меня вопрос по обновлению   -  person    schedule 07.02.2014
comment
@trojanfoe: пожалуйста, удалите possible duplicate, потому что это другая проблема, и если другой пользователь SO покажет это в вопросе, они не будут пытаться ответить на него, поэтому, пожалуйста.   -  person    schedule 07.02.2014
comment
@Optimistic Хорошо, готово.   -  person trojanfoe    schedule 07.02.2014
comment
@trojanfoe: Большое спасибо :), если вы знаете что-то, связанное с этим, пожалуйста, помогите мне, я застрял в этой проблеме с 10 дней.   -  person Toseef Khilji    schedule 07.02.2014
comment
@Optimistic Вы решили эту проблему? Если вы решили, пожалуйста, опубликуйте свое решение.   -  person Sharme    schedule 03.06.2014


Ответы (3)


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

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

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

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

person Stephen Darlington    schedule 07.02.2014
comment
@stephen: если я удалю beginBackgroundTaskWithExpirationHandler, позволит ли ОС продолжить работу моего приложения? - person Toseef Khilji; 07.02.2014
comment
@Virussmca Смотрите мой первый абзац. Приложения не продолжают работать в фоновом режиме — это не то, как работает iOS. Однако, когда ваше приложение находится в фоновом режиме, iOS разбудит ваше приложение, когда произойдет что-то интересное, и вызовет делегатов вашего основного местоположения. - person Stephen Darlington; 07.02.2014
comment
@StephenDarlington: я отредактировал вопрос, теперь я удалил beginBackgroundTaskWithExpirationHandler: как вы говорите, но теперь strtUpdateLocation не вызывается? - person ; 07.02.2014
comment
Хороший улов @StephenDarlington, я полностью пропустил это. Неправильно, так неправильно. Никогда не собирался работать. Оптимистично, вы, по сути, ждали истечения срока действия backgroundTask, а затем сказали: «Эй, подождите, сделайте эту задачу». Вот почему у тебя было всего 40 секунд. При правильном выполнении у вас получалось около 10 минут. В документах теперь указывается некоторое количество минут, не обязательно 10. - person Dean Davids; 07.02.2014
comment
@Optimistic Можете ли вы поставить точку останова в методе удаленного уведомления и посмотреть, будет ли он когда-либо вызываться? Вы не запрашиваете привилегию фоновой выборки и никогда не вызываете обработчик завершения выборки. - person Stephen Darlington; 07.02.2014
comment
@StephenDarlington: Во-первых, извините за задержку с ответом, из-за выходных я был включен. и Да, didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler вызывается, и менеджер местоположений тоже запускается, но только на 40 или 30 секунд, могу ли я продлить это? - person ; 10.02.2014
comment
@Optimistic Предполагая, что все остальное настроено правильно, делегат диспетчера местоположения вызывается даже когда ваше приложение находится в фоновом режиме. Нет, вы не можете продлить 30-40 секунд, но это не проблема, потому что вам это не нужно! - person Stephen Darlington; 10.02.2014
comment
@StephenDarlington: как вы предлагаете, я удалил beginBackgroundTaskWithExpirationHandler и написал только [self.locationManager startUpdatingLocation];, но делегат местоположения приложения все равно перестал отправлять местоположение. Можете ли вы сказать мне, что написать в методе didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler? - person ; 10.02.2014
comment
@Optimistic 1. Что произойдет, если вы поместите startUpdatingLocation в метод, который запускается, когда приложение работает на переднем плане? Тогда это работает? 2. Вы уверены, что при запуске приложения в фоновом режиме установлено значение self.locationManager? - person Stephen Darlington; 10.02.2014
comment
@StephenDarlington: Да, если я начну обновление местоположения на переднем плане, ОС не уничтожит приложение, приложение будет работать непрерывно. Но если у меня есть вызов startUpdatingLocation в фоновом режиме, когда приложение с push-уведомлением запускается только в течение 30–40 секунд ... и приложение требует запуска в фоновом режиме. - person ; 10.02.2014
comment
@StephenDarlington: если вам нужно, я отправлю код загрузки. - person ; 10.02.2014
comment
@Optimistic МНОГОЗАДАЧНОСТЬ В IOS РАБОТАЕТ НЕ ТАК! Вы не можете ожидать более 30 секунд в фоновом режиме — и вам нужно больше этого. Core Location разбудит ваше приложение, если оно не находится на переднем плане. - person Stephen Darlington; 10.02.2014
comment
@Optimistic Запуск обновлений местоположения на переднем плане (а затем перевод приложения в фоновый режим) проверит, позволяет ли Apple запускать обновления местоположения в фоновом режиме. Я думаю, что это было бы разумным ограничением, если бы это не было разрешено. - person Stephen Darlington; 10.02.2014
comment
Согласно 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. ТАК, должно ли приложение работать в фоновом режиме до тех пор, пока я включил режим фона местоположения,? - person ; 11.02.2014
comment
Вроде, как бы, что-то вроде. После вызова didUpdateLocation: ваше приложение снова перейдет в спящий режим. Но это будет происходить каждый раз, когда происходит обновление местоположения, поэтому это имеет тот же эффект, что и постоянная работа в фоновом режиме. - person Stephen Darlington; 11.02.2014

Некоторое время назад я немного поработал с 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;
     }
}
person Dean Davids    schedule 07.02.2014
comment
@David Спасибо, что вы сильно различаетесь за это объяснение, но у меня есть один вопрос: если приложение запущено, а диспетчер местоположений также запущен, то почему приложение было остановлено ОС? Могу ли я получить больше времени, используя какой-нибудь backgroundFatcher или какой-либо другой метод? - person ; 10.02.2014
comment
Многие ошибочно полагают, что фоновое выполнение в iOS — это лицензия на свободное выполнение в фоновом режиме. Правда в том, что iOS очень разборчива в том, что она позволяет. Вы должны думать об этом больше, так как вам разрешено прослушивать определенные события, в данном случае события местоположения, и вам дается короткое окно для ответа. При любых обстоятельствах ОС оставляет за собой право приостановить или закрыть ваше приложение, когда сочтет это целесообразным, и будет делать это часто в зависимости от нагрузки на память, процессор, батарею и других систем. - person Dean Davids; 10.02.2014
comment
В вашем случае @stephendarlington решил вашу проблему напрямую. Вы неправильно используете идентификатор фоновой задачи. Если вы исправили это, вам должно быть предоставлено время, необходимое для завершения. Вы должны отметить его ответ как решение. Если у вас все еще есть проблемы, вероятно, пришло время для новых вопросов. - person Dean Davids; 10.02.2014
comment
@davids: да, я удалил beginBackgroundTaskWithExpirationHandler: и написал только [self.locationManager startUpdatingLocation]; в соответствии с предложением stephendarlington, но все равно приложение убивают. Было бы здорово, если бы вы показали мне правильное использование идентификатора фоновой задачи или просто указали просто ссылки. - person ; 10.02.2014
comment
Я добавил пример формы истечения срока действия. Также обратите внимание, что я изменил метод, чтобы не включать fetchHandler. Не похоже, что вы используете это в своем случае, нет? - person Dean Davids; 10.02.2014
comment
Да, я использую этот метод. Используя ваш обновленный код didReceiveRemoteNotification, я не могу получить бесшумное push-уведомление, и этот метод не вызывается, когда я отправляю уведомление на устройство, даже на переднем плане, используя эту полезную нагрузку {"aps" : {"content-available" : 1},"SilentPush" : "4"}. - person ; 11.02.2014
comment
Как я знаю, didReceiveRemoteNotification вызывается только тогда, когда пользователь нажимает на уведомление или приходит уведомление, и если приложение находится на переднем плане. Если я отправляю обычную полезную нагрузку, например ( { "aps": { "alert": "Rush Hour", "sound": "default" }, "id": 1234} ), чем это работает, уведомление отображается в центре уведомлений, и если пользователь нажимает это приложение выходит на передний план, и начинается обновление местоположения. - person ; 11.02.2014
comment
Но мне не нужно для этого взаимодействие с пользователем, поэтому я использую тихие push-уведомления, и для этого требуется didReceiveRemoteNotification с fetchHandler. - person ; 11.02.2014
comment
Согласно Apple Doc: если ваше приложение приостановлено или не запущено, система просыпается или запускает ваше приложение и переводит его в фоновое рабочее состояние перед вызовом метода. ТАК, должно ли приложение работать в фоновом режиме до тех пор, пока я включил режим фона местоположения? - person ; 11.02.2014
comment
@DeanDavids Отличный ответ, но один вопрос: если вы используете существенное изменение и хотите запросить обновленное, более точное текущее местоположение (скажем, точное в пределах нескольких метров), как бы вы это вызвали? - person YWCA Hello; 20.07.2016
comment
Событие @YWCAHello ImportantChange уведомит/разбудит ваше приложение. Это не имеет никакого отношения к тому, какую точность вы получите. Ваш диспетчер местоположения начнет получать события, и они будут соответствовать настройкам, которые вы выбрали при его инициализации. Если вы хотите запросить большую точность, вам нужно будет сбросить параметры в вашем диспетчере местоположений и дождаться результата, отвечая по своему желанию. - person Dean Davids; 21.07.2016

Метод beginBackgroundTaskWithExpirationHandler: дает приложению время для работы, но это не означает, что задача может выполняться вечно.
Ваше приложение убито, потому что у вас явно истекает время, как указано в журнале сбоев.< br> Вы должны получить тот же результат, просто запросив обновление местоположения без переноса в обработчик истечения срока действия.
Я помню, что в старых системах я справлялся с чем-то подобным, запуская обновления местоположения в фоновом режиме с помощью мониторинга региона.
[EDIT]
Чтобы добиться наилучших результатов при игре с местоположением, я предлагаю вам использовать настоящие устройства, и, возможно, вы ими являетесь.

person Andrea    schedule 07.02.2014
comment
Согласно Apple Doc: если ваше приложение приостановлено или не запущено, система просыпается или запускает ваше приложение и переводит его в фоновое рабочее состояние перед вызовом метода. ТАК, должно ли приложение работать в фоновом режиме до тех пор, пока я включил фоновый режим местоположения? - person ; 11.02.2014
comment
Извините, но я не понимаю, о чем вы спрашиваете. Apple doc также говорит:When this method is called, your app has up to 30 seconds of wall-clock time to perform the download operation and call the specified completion handler block. In practice, your app should call the handler block as soon as possible after downloading the needed data. If you do not call the handler in time, your app is suspended. More importantly, the system uses the elapsed time to calculate power usage and data costs for your app’s background downloads - person Andrea; 11.02.2014