只保留一个后台任务

时间:2015-11-18 17:28:41

标签: ios uibackgroundtask

我正在开发一个使用后台任务的应用程序,每隔20秒跟踪一次用户位置。一切都很好,除了当我在后台进入应用程序时,会创建一个新的后台任务,这样我就可以拥有最终的多个正在运行的后台任务。 我尝试在[[UIApplication sharedApplication] endBackgroundTask:bgTask];中添加applicationWillEnterForeground,但这无效。 关键是我想在进入应用程序时无效/禁用所有正在运行的后台任务,并在我进入后台时创建一个新任务,或者只保持一个后台任务运行。

- (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
}

2 个答案:

答案 0 :(得分:2)

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

-

一个无关的观察,但你不需要那个runloop的东西。只需在主线程/ runloop(使用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__);
}

现在,在您的代码示例中,计时器不是重复计时器,因此如果您只想触发一次计时器,则将repeats设置为NO,但请确保{ {1}}然后结束了后台任务(如果你不打算做任何其他事情,那么保持app活着没有任何意义)。

BTW,请确保您在设备上运行此代码,并且不要从Xcode运行它(因为附加到Xcode会更改应用程序的后台行为)。

答案 1 :(得分:1)

指定位置背景模式 使用NSTimer在后​​台使用UIApplication:beginBackgroundTaskWithExpirationHandler: 如果n小于UIApplication:backgroundTimeRemaining,它将正常工作,如果n更大,则应该在没有剩余时间之前再次启用(和禁用)location manager避免后台任务被杀死。

这确实有效,因为location是三种允许的后台执行类型之一。

注意:通过在无法运行的模拟器中进行测试可以节省一些时间,在手机上正常工作。