7

即使应用程序在后台运行,我也想运行一些长时间运行的进程。我正在调用应用程序的beginBackgroundTaskWithExpirationHandler:方法,并且在 expireBlock 中我正在调用应用程序的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: 或 beginBackgroundTaskWithExpirationHandler: 方法的每次调用都会生成一个唯一的令牌以与相应的任务相关联。当你的应用完成一个任务时,它必须调用 endBackgroundTask: 方法并带有相应的令牌,让系统知道任务已经完成。未能为后台任务调用 endBackgroundTask: 方法将导致您的应用程序终止。如果您在启动任务时提供了过期处理程序,系统会调用该处理程序并给您最后一次机会来结束任务并避免终止。

所以,根据这个,如果我不调用endBackgroundTask:我的应用程序将被终止,这没关系。

我的问题是:在我当前的实现中,如果我endBackgroundTask:在 expireHandler 块中调用并且我的调度队列的任务没有完成怎么办?我的应用程序将被终止或将被暂停?

谢谢

4

5 回答 5

14

以下是一些场景,您需要在使用时处理这些场景,beginBackgroundTaskWithExpirationHandler否则您的应用程序将terminate.

场景 1:您的应用程序正在运行Foreground。你开始beginBackgroundTaskWithExpirationHandler 然后进入Background模式。您的应用程序可以长时间保持活力。

场景 2:您的应用程序正在运行Foreground。你开始beginBackgroundTaskWithExpirationHandler 然后进入Background模式。然后回到Foreground模式,你没有调用endBackgroundTask你的应用程序execute background queue,所以它会扩展下一个进程3 minute(在 IOS 7 引入之后。在 IOS 7 之前,进程执行时间是 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];

}
于 2015-04-29T05:45:27.120 回答
1

如果您不调用endBackgroundTask过期处理程序,您的应用程序将被终止。

一旦你调用endBackgroundTask你的过期处理程序,你只是告诉操作系统“好的,我已经完成了保存关键任务。现在由你决定。” 之后,您的应用程序可能会暂停一段时间并稍后终止,或者可能会立即终止,具体取决于系统资源。

于 2015-04-29T05:57:06.543 回答
0

您的演示代码的问题是您在用户任务过程中丢失了 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;
});
于 2020-04-05T10:08:32.797 回答
0

@Jatin Patel的回答很好。只需使用以下代码更改实现,ApplicationWillEnterForeground以便您的应用程序永远不会终止

DispatchQueue.global(qos: .background).async {
    // sends registration to background queue
    application.endBackgroundTask(self.bgTask)
    self.bgTask = UIBackgroundTaskIdentifier.invalid
}
于 2020-05-01T06:47:42.370 回答
0

我的应用程序在后台播放音乐,我收到了这个错误。

[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;
     }];
}
于 2021-10-30T14:00:12.210 回答