beginBackgroundTaskWithExpirationHandler вызывает endBackgroundTask, но не завершает процесс

У меня есть какой-то длительный процесс, который я хочу запустить, даже если приложение работает в фоновом режиме. Я вызываю метод приложения beginBackgroundTaskWithExpirationHandler:, а в блоке expireBlock я вызываю приложение endBackgroundTask. Вот реализация:

__block UIBackgroundTaskIdentifier task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    [[UIApplication sharedApplication] endBackgroundTask:task];
    task = UIBackgroundTaskInvalid;
}];
dispatch_queue_t queue = dispatch_queue_create("com.test.test1234", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    // My Task goes here
});

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

В документе Apple говорится:

При каждом вызове метода beginBackgroundTaskWithName:expirationHandler: или beginBackgroundTaskWithExpirationHandler: создается уникальный токен, связываемый с соответствующей задачей. Когда ваше приложение завершает задачу, оно должно вызвать метод endBackgroundTask: с соответствующим токеном, чтобы система знала, что задача выполнена. Если вы не вызовете метод endBackgroundTask: для фоновой задачи, это приведет к завершению работы вашего приложения. Если вы предоставили обработчик срока действия при запуске задачи, система вызывает этот обработчик и дает вам последний шанс завершить задачу и избежать завершения.

Итак, в соответствии с этим, если я не вызову endBackgroundTask:, мое приложение будет закрыто, и это нормально.

Мой вопрос: с моей текущей реализацией, что, если я вызову endBackgroundTask: в блоке expireHandler, и задача моей очереди отправки не будет завершена? Мое приложение будет прекращено или будет приостановлено?

Благодарность


person Kapil Choubisa    schedule 28.04.2015    source источник
comment
если вы вызвали endBackgroundTask: в любом месте вашей очереди. он просто приостановил ваше приложение, поместив приложение в спящий режим.   -  person chirag shah    schedule 28.04.2015
comment
@chiragshah, так что это не может быть причиной того, что сторожевой таймер убьет мое приложение, верно? сторожевой таймер убьет приложение, только если я пропущу endBackgroundTask, верно?   -  person Kapil Choubisa    schedule 28.04.2015
comment
да, я делаю то же самое, думаю, в моем приложении это может работать нормально   -  person chirag shah    schedule 28.04.2015


Ответы (4)


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

Сценарий 1. Ваше приложение работает в Foreground. вы начинаете beginBackgroundTaskWithExpirationHandler, затем входите в режим Background. ваше приложение будет жить долго.

Сценарий 2. Ваше приложение работает в Foreground. вы начинаете beginBackgroundTaskWithExpirationHandler, затем входите в режим Background. Затем вернитесь в режим Foreground, и вы не вызываете endBackgroundTask, тогда ваше приложение по-прежнему execute background queue, поэтому оно расширит этот процесс для следующего 3 minute (после введения IOS 7. до IOS 7 время выполнения процесса составляло 10 минут). поэтому вам необходимо отменить фоновую очередь и задачу выйти из фоновой очереди и войти в очередь переднего плана.

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

Шаг 1: объявите __block UIBackgroundTaskIdentifier bgTask глобальной переменной.

Шаг 2. Чтобы добавить следующий код в applicationDidEnterBackground.

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

         bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
         bgTask = UIBackgroundTaskInvalid;
          }];

}

Шаг 3. Остановите обработчик фоновых задач, как только приложения перейдут в режим переднего плана.

- (void)applicationWillEnterForeground:(UIApplication *)application {
  // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.

  [[UIApplication sharedApplication] endBackgroundTask:bgTask];

}
person Jatin Patel - JP    schedule 29.04.2015
comment
Спасибо за ответ. В сценарии 2, что, если я не вызову endBackgroundTask, когда приложение перейдет на передний план? Я уже вызываю endBackgroundTask в блоке истечения срока действия. - person Kapil Choubisa; 29.04.2015
comment
Компилятор поймет, что ваша фоновая очередь все еще выполняется, поэтому ее выполнение продлится до следующих 3 минут после того, как этот компилятор завершит ваше приложение. это сценарий, который происходит со мной. - person Jatin Patel - JP; 29.04.2015

Если вы не вызовете endBackgroundTask в обработчике истечения срока действия, ваше приложение будет закрыто.

Как только вы вызываете endBackgroundTask в своем обработчике истечения срока действия, вы только сообщаете ОС: «Хорошо, я закончил с сохранением важных задач. Теперь это зависит от вас». После этого ваше приложение может быть приостановлено на некоторое время и прекращено позже, или оно может быть прекращено немедленно в зависимости от системных ресурсов.

person John Estropia    schedule 29.04.2015

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

вы должны использовать, как показано ниже:

__block UIBackgroundTaskIdentifier task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    [[UIApplication sharedApplication] endBackgroundTask:task];
    task = UIBackgroundTaskInvalid;
}];
dispatch_queue_t queue = dispatch_queue_create("com.test.test1234", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    // My Task goes here

    // add new
    [UIApplication sharedApplication] endBackgroundTask:task];
    task = UIBackgroundTaskInvalid;
});
person hiproz    schedule 05.04.2020

@Jatin Patel ответ хороший. Просто измените реализацию ApplicationWillEnterForeground с помощью приведенного ниже кода, чтобы ваше приложение никогда не завершалось.

DispatchQueue.global(qos: .background).async {
    // sends registration to background queue
    application.endBackgroundTask(self.bgTask)
    self.bgTask = UIBackgroundTaskIdentifier.invalid
}
person A R    schedule 01.05.2020