web-dev-qa-db-fra.com

Comment utiliser beginBackgroundTaskWithExpirationHandler pour une tâche déjà en cours d'exécution dans iOS

Dans mon application, je télécharge des images sur le serveur depuis l'iPhone. Pendant la synchronisation si l'utilisateur appuie sur le bouton d'accueil, l'application se fermera.

Je veux que l'application s'exécute en arrière-plan jusqu'à la fin de la synchronisation.

Ma question est: Comment puis-je ajouter une tâche en cours d'exécution avec "beginBackgroundTaskWithExpirationHandler"?

Veuillez partager vos idées.

38
Vardhan

J'ai utilisé le code ci-dessous pour le gestionnaire de tâches en arrière-plan:

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

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

    [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];

    backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}];
15
Vardhan

Malgré son nom, beginBackgroundTaskWithExpirationHandler: ne "commence" pas réellement une tâche. Il serait peut-être préférable de penser à "s'inscrire ..." plutôt qu'à "commencer ..." Vous dites simplement au système que vous êtes en train de faire quelque chose qui voudrait terminer si cela vous convient.

Plusieurs points:

  • Dans presque tous les cas, vous souhaitez appeler beginBackgroundTaskWithExpirationHandler: lorsque vous commencez à faire ce que vous voulez faire, puis endBackgroundTask: lorsque vous avez terminé. Vous ne les appelez presque jamais au moment où l'utilisateur appuie sur le bouton Accueil (sauf si c'est le moment où vous démarrez la tâche, comme l'enregistrement de quelque chose sur le serveur).

  • Vous pouvez avoir autant de "tâches d'arrière-plan" que vous souhaitez ouvrir à la fois. Ils ne coûtent rien. Ils sont comme retenir les comptes. Tant que vous en avez encore un ouvert (un "début" qui n'est pas arrivé à sa "fin"), le système ne considérera qu'une demande pour plus de temps. L'appel des méthodes "begin" et "end" coûte très peu, donc utilisez-les pour mettre en parenthèse tout ce que vous voulez plus de temps pour terminer.

  • Il n'y a aucun moyen d'être certain que vous arriverez à terminer votre synchronisation. Ces méthodes ne font qu'une demande. Le système d'exploitation peut toujours refuser cette demande. Le système d'exploitation peut toujours vous tuer chaque fois qu'il a besoin de ressources supplémentaires. L'OS n'est pas infiniment patient; si vous prenez trop de temps (environ 10 minutes en général), cela vous tuera de toute façon après avoir appelé votre gestionnaire d'expiration. Le système d'exploitation peut vous tuer parce que le téléphone est éteint. Votre application doit être en mesure de ne pas finir. Le système d'exploitation vous donne simplement cette capacité afin que vous puissiez améliorer l'expérience utilisateur.

99
Rob Napier

Voici comment j'ai utilisé beginBackgroundTaskWithExpirationHandler, y compris l'appel à la méthode à effectuer en arrière-plan. Cela inclut le code pour démarrer la nouvelle tâche asynchrone. Ce n'est donc pas exactement ce qui est demandé dans la question. Ajout de code ci-dessous, en pensant que quelqu'un pourrait rechercher ce scénario:

- (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...");

}
9
Joe M

Jetez un œil à cette page, Apple décrivez comment garder l'application iOS non suspendue lorsqu'elle est en arrière-plan. Documentation Apple

Et voici ce que j'ai implémenté:

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 ===");
    //it’s important to stop background task when we do not need it anymore
    [[UIApplication sharedApplication] endBackgroundTask:UIBackgroundTaskInvalid];  
}
6
Duyen-Hoa

Voici une légère variation sur les autres réponses que j'utilise lorsque l'application passe en arrière-plan et je dois faire quelque chose sur le fil principal:

- (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;
    });
}
4
BuvinJ

Vous devez exécuter votre tâche après beginBackgroundTaskWithExpirationHandler ou capturer une notification lorsque l'application change l'état de l'application en arrière-plan, puis annulez votre tâche actuelle et réexécutez avec beginBackgroundTaskWithExpirationHandler.

1
tikhop