只保留一个后台任务
Keep just one background task
我正在开发一个应用程序,它使用后台任务每 20 秒跟踪一次用户位置。一切都很好,除了当我在后台进入应用程序时,会创建一个新的后台任务,这样我就可以在最终的多个后台任务中得到 运行。
我试图在 applicationWillEnterForeground
中添加 [[UIApplication sharedApplication] endBackgroundTask:bgTask];
,但那没有任何作用。
关键是我想在进入应用程序时 invalidate/disable 所有 运行 后台任务,并在进入后台时创建一个新任务,或者只保留一个后台任务 运行。
- (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
}
指定位置背景模式
使用 UIApplication:beginBackgroundTaskWithExpirationHandler:
在后台使用 NSTimer
如果 n
小于 UIApplication:backgroundTimeRemaining ,它会工作得很好,如果 n
更大, location manager
应该在没有之前再次启用(和禁用)避免后台任务被杀死的剩余时间。
这确实有效,因为位置是三种允许的后台执行类型之一。
注意:通过在模拟器中测试它不起作用而浪费了一些时间,在 phone 上运行良好。
我建议将 UIBackgroundTaskIdentifier
更改为应用委托 class 的 属性,并在 didFinishLaunchingWithOptions
中将其初始化为 UIBackgroundTaskInvalid
。然后,在你的其他应用程序委托方法中,你可以只检查这个 属性 的值来确定是否有后台任务标识符结束。
--
一个不相关的观察,但您不需要 运行循环的东西。只需在主 thread/runloop 上安排计时器(使用 scheduledTimerWithTimeInterval
)并摆脱所有 运行 循环的东西(因为你已经将它添加到主 运行 循环和运行loop 已经 运行ning).
例如,假设我想在应用程序处于后台时每 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 会改变背景行为的应用程序)。
我正在开发一个应用程序,它使用后台任务每 20 秒跟踪一次用户位置。一切都很好,除了当我在后台进入应用程序时,会创建一个新的后台任务,这样我就可以在最终的多个后台任务中得到 运行。
我试图在 applicationWillEnterForeground
中添加 [[UIApplication sharedApplication] endBackgroundTask:bgTask];
,但那没有任何作用。
关键是我想在进入应用程序时 invalidate/disable 所有 运行 后台任务,并在进入后台时创建一个新任务,或者只保留一个后台任务 运行。
- (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
}
指定位置背景模式
使用 UIApplication:beginBackgroundTaskWithExpirationHandler:
在后台使用 NSTimer
如果 n
小于 UIApplication:backgroundTimeRemaining ,它会工作得很好,如果 n
更大, location manager
应该在没有之前再次启用(和禁用)避免后台任务被杀死的剩余时间。
这确实有效,因为位置是三种允许的后台执行类型之一。
注意:通过在模拟器中测试它不起作用而浪费了一些时间,在 phone 上运行良好。
我建议将 UIBackgroundTaskIdentifier
更改为应用委托 class 的 属性,并在 didFinishLaunchingWithOptions
中将其初始化为 UIBackgroundTaskInvalid
。然后,在你的其他应用程序委托方法中,你可以只检查这个 属性 的值来确定是否有后台任务标识符结束。
--
一个不相关的观察,但您不需要 运行循环的东西。只需在主 thread/runloop 上安排计时器(使用 scheduledTimerWithTimeInterval
)并摆脱所有 运行 循环的东西(因为你已经将它添加到主 运行 循环和运行loop 已经 运行ning).
例如,假设我想在应用程序处于后台时每 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 会改变背景行为的应用程序)。