我正在开发一个使用后台任务的应用程序,每隔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
}
答案 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是三种允许的后台执行类型之一。
注意:通过在无法运行的模拟器中进行测试可以节省一些时间,在手机上正常工作。