我正在开发一个应用程序,该应用程序使用后台任务每 20 秒跟踪一次用户位置。一切都很好,除了当我在后台进入应用程序时,会创建一个新的后台任务,这样我就可以在最终运行多个后台任务。
我尝试在 applicationWillEnterForeground
中添加 [[UIApplication sharedApplication] endBackgroundTask:bgTask];
,但这没有任何作用。
重点是,我想在进入应用程序时使所有正在运行的后台任务无效/禁用,并在进入后台时创建一个新任务,或者仅保持一个后台任务运行。
- (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
}
最佳答案
我建议将 UIBackgroundTaskIdentifier
更改为应用委托(delegate)类的属性,并在 didFinishLaunchingWithOptions
中将其初始化为 UIBackgroundTaskInvalid
。然后,在您的其他应用程序委托(delegate)方法中,您只需检查此属性的值即可确定是否有要结束的后台任务标识符。
--
一个不相关的观察结果,但你不需要运行循环的东西。只需在主线程/运行循环上安排计时器(使用 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__);
}
现在,在您的代码示例中,计时器不是重复计时器,因此如果您只想触发计时器一次,请将 repeats
设置为 NO
,但随后请确保 startTracking
结束了后台任务(如果您不打算执行任何其他操作,则保持应用程序处于事件状态是没有意义的)。
顺便说一句,请确保您在设备上运行此代码,并且不要从 Xcode 运行它(因为附加到 Xcode 会更改应用程序的后台行为)。
关于ios - 只保留一项后台任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33786515/