Как использовать startBackgroundTaskWithExpirationHandler для уже запущенной задачи в iOS

В моем приложении я загружаю изображения на сервер с iPhone. Во время синхронизации, если пользователь нажимает кнопку "домой", приложение закрывается.

Я хочу, чтобы приложение работало в фоновом режиме до завершения синхронизации.

Мой вопрос: как я могу добавить текущую задачу с "beginBackgroundTaskWithExpirationHandler"?

Поделитесь своими идеями.

Ответ 1

Я использовал ниже код для обработчика фоновых задач:

__block UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

    NSLog(@"Background Time:%f",[[UIApplication sharedApplication] backgroundTimeRemaining]);

    [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];

    backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}];

Ответ 2

Несмотря на свое название, beginBackgroundTaskWithExpirationHandler: фактически не запускает задачу. Возможно, лучше подумать, как "зарегистрироваться...", а не "начать...". Вы просто сообщаете системе, что вы в середине делаете что-то, что хотели бы завершить, если это нормально.

Несколько точек:

  • Почти во всех случаях вы вызываете beginBackgroundTaskWithExpirationHandler:, когда начинаете делать то, что хотите, а затем endBackgroundTask:, когда вы закончите. Вы почти никогда не вызываете их в той точке, в которой пользователь нажимает кнопку "Домой" (если только эта точка не запускается, например, что-то на сервере).

  • У вас может быть столько "фоновых задач", сколько вы хотите открывать одновременно. Они ничего не стоят. Они напоминают счет. Пока у вас все еще есть один открытый ( "начало", которое не доходит до его "конца" ), тогда система будет считать, что запрос больше времени. Существует очень мало затрат на вызов методов "начать" и "конец", поэтому используйте их, чтобы скопировать все, что вы хотите, чтобы дополнительное время было завершено.

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

Ответ 3

Здесь, как я использовал beginBackgroundTaskWithExpirationHandler, включая вызов метода, который должен выполняться в фоновом режиме. Это включает в себя код для запуска новой задачи async. Поэтому не совсем то, что задается в вопросе. Добавляя код ниже, думая, что кто-то может искать этот сценарий:

- (void)didReceiveObjList:(CFDataRef)message
{
  // Received Object List. Processing the list can take a few seconds.
  // So doing it in separate thread with expiration handler to ensure it gets some time to do     the task even if the app goes to background.

  UIApplication *application = [UIApplication sharedApplication];
  __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
    [application endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
}];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      [self processObjList:message];

    [application endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
  });

  NSLog(@"Main Thread proceeding...");

}

Ответ 4

Взгляните на эту страницу, Apple расскажет о том, как приостановить приложение iOS, когда оно находится в фоновом режиме. Документация Apple

И вот что я реализовал:

UIBackgroundTaskIdentifier bgTask;

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    NSLog(@"=== DID ENTER BACKGROUND ===");
    bgTask = [[UIApplication  sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
        // [self askToRunMoreBackgroundTask]; This code seems to be unnecessary. I'll verify it.
    }];

    if (bgTask == UIBackgroundTaskInvalid) {
        NSLog(@"This application does not support background mode");
    } else {
        //if application supports background mode, we'll see this log.
        NSLog(@"Application will continue to run in background");  
    }
}

/*

- (void)askToRunMoreBackgroundTask
{
    [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    NSLog(@"restart background long-running task");
    bgTask = [[UIApplication  sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
        [self askToRunMoreBackgroundTask]; 
    }];

}

*/

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    NSLog(@"=== GOING BACK FROM IDLE MODE ===");
    //its important to stop background task when we do not need it anymore
    [[UIApplication sharedApplication] endBackgroundTask:UIBackgroundTaskInvalid];  
}

Ответ 5

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

- (void)applicationWillResignActive:(UIApplication *)application
{
     UIApplication *app = [UIApplication sharedApplication];

    // Register auto expiring background task
    __block UIBackgroundTaskIdentifier bgTaskId =
        [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTaskId];
        bgTaskId = UIBackgroundTaskInvalid;
    }];

    // Execute background task on the main thread
    dispatch_async( dispatch_get_main_queue(), ^{
        // ------------------
        // DO SOMETHING INVOLVING THE WEBVIEW - WHICH MUST OCCUR ON THE MAIN THREAD!
        // ------------------
        [app endBackgroundTask:bgTaskId];
        bgTaskId = UIBackgroundTaskInvalid;
    });
}

Ответ 6

Вы должны запустить свою задачу после beginBackgroundTaskWithExpirationHandler или захватить уведомление, когда приложение изменит состояние приложения на фоновое, а затем отмените текущую задачу и снова запустите с помощью beginBackgroundTaskWithExpirationHandler.