2

我正在开发一个应用程序,它使用后台任务每 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
}
4

2 回答 2

2

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

--

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

例如,假设我想在应用程序处于后台时每 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 会更改应用程序的后台行为)。

于 2015-11-18T17:50:43.247 回答
1

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

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

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

于 2015-11-18T17:50:31.413 回答