beginBackgroundTaskWithExpirationHandler, вызывающий endBackgroundTask, но не завершающий процесс

У меня есть длительный процесс, который я хочу запустить, даже если приложение работает в фоновом режиме. Я звоню в приложение beginBackgroundTaskWithExpirationHandler: метод и в expirationBlock я вызываю приложения 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: в блоке expirationHandler и моя очередь отправки не завершена? Мое приложение будет прекращено или будет приостановлено?

Спасибо

5 ответов

Вот несколько сценариев, которые вы должны обработать при использовании 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];

}

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

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

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

[BackgroundTask] Фоновая задача 1 ("Вызывается UIKitCore, из __47-[UIApplication _applicationDidEnterBackground] block_invoke") была создана более 30 секунд назад. В приложениях, работающих в фоновом режиме, это создает риск завершения. Не забывайте своевременно вызывать UIApplication.endBackgroundTask(:) для своей задачи, чтобы избежать этого.

Я исправил это, используя этот код:

      - (void)applicationDidEnterBackground:(UIApplication *)application
{
    backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self->backgroundTask];
        self->backgroundTask = UIBackgroundTaskInvalid;
     }];
}

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

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

Проблема вашего демонстрационного кода в том, что вы теряете 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;
});
Другие вопросы по тегам