我有一些长时间运行的进程,即使应用程序在后台运行,我也要运行这些进程。我正在调用应用程序的beginBackgroundTaskWithExpirationHandler:
方法,并且在expirationBlock中我正在调用该应用程序的endBackgroundTask
。以下是实现:
__block UIBackgroundTaskIdentifier task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}];
dispatch_queue_t queue = dispatch_queue_create("com.test.test1234", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// My Task goes here
});
在某些情况下,我的串行队列有更多的任务要执行,无法在系统提供的时间内完成。因此,过期块将执行,因为我正在结束UIBackgroundTaskIdentifier
,但不会停止调度过程(我甚至不能取消调度)。
苹果公司的文件说:
对beginBackgroundTaskWithName:expirationHandler:或beginBackbackgroundTaskWithExpirationHandler:方法的每次调用都会生成一个唯一的令牌,用于与相应的任务关联。当你的应用程序完成任务时,它必须使用相应的令牌调用endBackgroundTask:方法,让系统知道任务已经完成。未能为后台任务调用endBackgroundTask:方法将导致应用程序终止。如果您在启动任务时提供了一个过期处理程序,系统会调用该处理程序,并给您最后一次机会来结束任务并避免终止。
因此,根据这一点,如果我不调用endBackgroundTask:
,我的应用程序将被终止,这是可以的。
我的问题是:在我当前的实现中,如果我在expirationHandler块中调用endBackgroundTask:
,而我的调度队列的任务没有完成,该怎么办?我的应用程序将被终止还是将被暂停?
感谢
以下是一些场景,您需要在使用beginBackgroundTaskWithExpirationHandler
时处理这些场景,否则您的应用程序将使用terminate
。
场景1:您的应用程序正在Foreground
中运行。您先启动beginBackgroundTaskWithExpirationHandler
,然后进入Background
模式。你的应用程序可以长期运行。
场景2:您的应用程序正在Foreground
中运行。您先启动beginBackgroundTaskWithExpirationHandler
,然后进入Background
模式。然后回到Foreground
模式,并且您没有调用endBackgroundTask
,那么您的应用程序仍然是execute background queue
,因此它将扩展该进程到下一个3 minute
(在IOS7引入之后。在IOS7之前,进程执行时间为10分钟)。所以您必须取消后台队列,任务从后台队列中出来并进入前台队列。
下面是向您展示的代码。什么是处理后台进程的最佳方式。
步骤1:将__block UIBackgroundTaskIdentifier bgTask声明为全局变量。
步骤2:在applicationDidEnterBackground中添加以下代码。
- (void)applicationDidEnterBackground:(UIApplication *)application {
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
bgTask = UIBackgroundTaskInvalid;
}];
}
第3步:应用程序进入前台模式后,停止后台任务处理程序。
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
}
如果您不在过期处理程序中调用endBackgroundTask
,您的应用程序将被终止。
一旦你在过期处理程序中调用endBackgroundTask
,你只会告诉操作系统"好吧,我完成了保存关键任务。现在由你决定。"之后,你的应用程序可能会暂停一段时间,稍后再终止,也可能会根据系统资源立即终止。
演示代码的问题是您在用户任务过程中丢失了endBackgroundTask。
你应该使用如下:
__block UIBackgroundTaskIdentifier task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}];
dispatch_queue_t queue = dispatch_queue_create("com.test.test1234", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// My Task goes here
// add new
[UIApplication sharedApplication] endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
});
@Jatin Patel答案很好。只需使用以下代码更改ApplicationWillEnterForeground
的实现,这样您的应用程序就永远不会终止
DispatchQueue.global(qos: .background).async {
// sends registration to background queue
application.endBackgroundTask(self.bgTask)
self.bgTask = UIBackgroundTaskIdentifier.invalid
}
我的应用程序在后台播放音乐,我收到了这个错误。
[BackgroundTask]后台任务1("由UIKitCore调用,来自__47-[UIApplication_applicationDidEnterBackground]block_invoke")创建于30秒前。在后台运行的应用程序中,这会产生终止的风险。请记住及时为您的任务调用UIApplication.endBackgroundTask(:)以避免这种情况。
我使用以下代码修复了它:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self->backgroundTask];
self->backgroundTask = UIBackgroundTaskInvalid;
}];
}