web-dev-qa-db-fra.com

NSTimer planifié lorsque l'application est en arrière-plan?

Comment les gens traitent-ils un NSTimer planifié lorsqu'une application est en arrière-plan?

Disons que je mets à jour quelque chose dans mon application toutes les heures. 

updateTimer = [NSTimer scheduledTimerWithTimeInterval:60.0*60.0 
target:self 
selector:@selector(updateStuff) 
userInfo:nil 
repeats:YES];

En arrière-plan, cette minuterie ne déclenche évidemment pas (?). Que devrait-il se passer lorsque l'utilisateur revient à l'application ..? La minuterie est-elle toujours en marche, avec les mêmes temps?

Et que se passerait-il si l'utilisateur revenait dans plus d'une heure? Cela déclenchera-t-il toutes les heures manquées ou attendra-t-il jusqu'à la prochaine mise à jour?

Ce que je voudrais, c’est mettre à jour immédiatement après l’application, si la date à laquelle elle aurait dû être renvoyée est dépassée. Est-ce possible?

43
cannyboy

Vous ne devriez pas résoudre ce problème en configurant une minuterie, car vous n'êtes autorisé à exécuter aucun code en arrière-plan. Imaginez ce qui se passera si l'utilisateur redémarre son iPhone entre-temps ou avec d'autres cas Edge.

Utilisez les méthodes applicationDidEnterBackground: et applicationWillEnterForeground: de votre AppDelegate pour obtenir le comportement souhaité. C'est beaucoup plus robuste, car cela fonctionnera également lorsque votre application sera complètement supprimée à cause d'un redémarrage ou d'une pression de la mémoire.

Vous pouvez enregistrer l'heure à laquelle la minuterie se déclenchera lorsque votre application passera à l'arrière-plan et vérifier si vous devez agir lorsque l'application revient au premier plan. Arrêtez et redémarrez également le chronomètre avec cette méthode. Pendant que votre application est en cours d'exécution, vous pouvez utiliser un minuteur pour déclencher la mise à jour au bon moment.

36
Mac_Cain13

Vous pouvez faire déclencher une minuterie en mode d’exécution en arrière-plan. Il y a quelques astuces:

Si vous êtes sur le fil principal:

{
    // Declare the start of a background task
    // If you do not do this then the mainRunLoop will stop
    // firing when the application enters the background
    self.backgroundTaskIdentifier =
    [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
    }];

    // Make sure you end the background task when you no longer need background execution:
    // [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];

    [NSTimer scheduledTimerWithTimeInterval:0.5
                                     target:self
                                   selector:@selector(timerDidFire:)
                                   userInfo:nil
                                    repeats:YES];
}

- (void) timerDidFire:(NSTimer *)timer
{
    // This method might be called when the application is in the background.
    // Ensure you do not do anything that will trigger the GPU (e.g. animations)
    // See: http://developer.Apple.com/library/ios/DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//Apple_ref/doc/uid/TP40007072-CH4-SW47
}

Remarques

  • Les applications ne bénéficient que d’environ 10 minutes d’exécution en arrière-plan (environ 3 minutes à compter de iOS 7). Après cela, la minuterie cessera de se déclencher.
  • À partir de iOS 7, lorsque le périphérique est verrouillé, l'application de premier plan est suspendue presque instantanément. La minuterie ne se déclenche pas après le verrouillage d'une application iOS 7.
41
Robert

Si vous ou quelqu'un d'autre cherchez comment exécuter NSTimer en arrière-plan dans Swift, ajoutez ce qui suit à votre délégué d'application:

var backgroundUpdateTask: UIBackgroundTaskIdentifier = 0


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    return true
}

func applicationWillResignActive(application: UIApplication) {
    self.backgroundUpdateTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
        self.endBackgroundUpdateTask()
    })
}

func endBackgroundUpdateTask() {
    UIApplication.sharedApplication().endBackgroundTask(self.backgroundUpdateTask)
    self.backgroundUpdateTask = UIBackgroundTaskInvalid
}

func applicationWillEnterForeground(application: UIApplication) {
    self.endBackgroundUpdateTask()
}

À votre santé!

14
gellieb

Sur iOS 8, vous pouvez faire fonctionner votre NSTimer lorsque l'application est en arrière-plan (même si l'iPhone est verrouillé) jusqu'à environ 3 minutes de manière simple:

implémentez simplement la méthode scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: comme d'habitude, là où vous en avez besoin. Dans AppDelegate, créez une propriété:

UIBackgroundTaskIdentifier backgroundUpdateTask

puis dans vos méthodes appDelegate:

- (void)applicationWillResignActive:(UIApplication *)application {
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    [self endBackgroundUpdateTask];
    }];
}

- (void) endBackgroundUpdateTask
{
    [[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];
    self.backgroundUpdateTask = UIBackgroundTaskInvalid;
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    [self endBackgroundUpdateTask];
}

après 3 minutes, la minuterie ne se déclenchera plus, lorsque l'application reviendra au premier plan, elle recommencera à se déclencher

5
Enrico Cupellini

En arrière-plan, cette minuterie ne tire évidemment pas

Ce post suggère que les choses ne sont pas aussi claires que cela. Vous devriez invalider vos minuteries lorsque votre application passe en arrière-plan et les redémarrer à nouveau au premier plan. Exécuter des tâches en arrière-plan est peut-être possible, mais cela pourrait vous tuer ...

Vous pouvez trouver une bonne documentation sur l'approche multitâche iOS ici .

5
jbat100

Vous devez ajouter votre minuterie dans la boucle d'exécution actuelle. 

[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
3
Maverick King
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
    loop = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(Update) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:loop forMode:NSRunLoopCommonModes];
2
vualoaithu

Vous devez ajouter un temporisateur dans la boucle d'exécution ( Référence - Page de développement Apple pour la compréhension de la boucle d'exécution).

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:@selector(updateTimer)  userInfo:nil  repeats:true];

[[NSRunLoop mainRunLoop] addTimer: timer forMode:NSRunLoopCommonModes];

//funcation

-(void) updateTimer{
NSLog(@"Timer update");

}

Vous devez ajouter une autorisation (modes d'arrière-plan requis) pour que Background fonctionne dans infoPlist.

1
M Abubaker Majeed

créer l'identifiant de tâche globale uibackground.

UIBackgroundTaskIdentifier bgRideTimerTask;

créez maintenant votre minuterie et ajoutez BGTaskIdentifier. N'oubliez pas de supprimer l'ancien BGTaskIdentifier lors de la création d'un nouvel objet Timer.

 [timerForRideTime invalidate];
 timerForRideTime = nil;

 bgRideTimerTask = UIBackgroundTaskInvalid;

 UIApplication *sharedApp = [UIApplication sharedApplication];       
 bgRideTimerTask = [sharedApp beginBackgroundTaskWithExpirationHandler:^{

                            }];
 timerForRideTime =  [NSTimer scheduledTimerWithTimeInterval:1.0
                                                                                 target:self
                                                                               selector:@selector(timerTicked:)
                                                                               userInfo:nil
                                                                                repeats:YES]; 
                                                   [[NSRunLoop currentRunLoop]addTimer:timerForRideTime forMode: UITrackingRunLoopMode];

Ici, cela fonctionnera pour moi même lorsque l'application passe en tâche de fond. Demandez-moi si vous rencontrez de nouveaux problèmes.

0
NIRAV BHAVSAR