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?
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.
Vous pouvez faire déclencher une minuterie en mode d’exécution en arrière-plan. Il y a quelques astuces:
beginBackgroundTaskWithExpirationHandler
. [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]
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
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é!
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
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 .
Vous devez ajouter votre minuterie dans la boucle d'exécution actuelle.
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
loop = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(Update) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:loop forMode:NSRunLoopCommonModes];
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.
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.