只保留一个后台任务

我正在开发一个应用程序,使用后台任务来保持每20秒跟踪用户位置。 一切都很好,只是当我在后台input应用程序时,会创build一个新的后台任务,这样我可以在最后的多个后台任务中运行。 我试着添加[[UIApplication sharedApplication] endBackgroundTask:bgTask];applicationWillEnterForeground ,但是什么都不做。 关键是我想在进入应用程序时使所有正在运行的后台任务无效化,并在后台input时创build一个新的后台任务,或者只保留一个后台任务。

 - (void)applicationDidEnterBackground:(UIApplication *)application { [self runBackgroundTask:10]; } -(void)runBackgroundTask: (int) time{ //check if application is in background mode if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { __block UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [[UIApplication sharedApplication] endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startTracking) userInfo:nil repeats:NO]; [[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; }); } } -(void)startTracking{ //Location manager code that is running well } 

我build议将UIBackgroundTaskIdentifier更改为应用程序委托类的属性,并将其初始化为didFinishLaunchingWithOptions UIBackgroundTaskInvalid 。 然后,在其他应用程序委托方法中,只需检查此属性的值,以确定是否存在后台任务标识符即可结束。

一个无关的观察,但你不需要runloop的东西。 只需在主线程/ runloop(with scheduledTimerWithTimeInterval )上安排计时器,并摆脱所有runloop的东西(因为你已经将它添加到主runloop,并且runloop已经在运行)。

例如,假设我想在应用程序处于后台时每隔10秒做一些事情,我会做如下的事情:

 @interface AppDelegate () @property (atomic) UIBackgroundTaskIdentifier bgTask; @property (nonatomic, weak) NSTimer *timer; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.bgTask = UIBackgroundTaskInvalid; return YES; } - (void)applicationDidEnterBackground:(UIApplication *)application { self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ if (self.bgTask != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:self.bgTask]; self.bgTask = UIBackgroundTaskInvalid; } }]; self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(startTracking) userInfo:nil repeats:YES]; } - (void)applicationWillEnterForeground:(UIApplication *)application { // invalidate the timer if still running [self.timer invalidate]; // end the background task if still present if (self.bgTask != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:self.bgTask]; self.bgTask = UIBackgroundTaskInvalid; } } - (void)startTracking{ NSLog(@"%s", __PRETTY_FUNCTION__); } 

现在,在你的代码示例中,定时器不是一个重复的定时器,所以如果你只想触发定时器一次,然后设置repeatsNO ,但是然后确保startTracking然后结束后台任务(保持应用程序活着,如果你不会做任何事情)。

顺便说一句,请确保您在设备上运行此代码,而不要从Xcode运行(因为附加到Xcode更改应用程序的后台行为)。

指定位置背景模式使用UIApplication:beginBackgroundTaskWithExpirationHandler:在后台使用NSTimer UIApplication:beginBackgroundTaskWithExpirationHandler:n小于UIApplication:backgroundTimeRemaining的情况下,它将工作得很好,如果n较大, location manager应该再次启用(和禁用)没有时间去避免后台任务被杀害。

这是可行的,因为位置是三种允许的后台执行types之一。

注意:通过在模拟器中testing不起作用而松了一些时间,在手机上工作正常。