beginBackgroundTaskWithExpirationHandler calling endBackgroundTask but not ending process
Asked Answered
J

5

7

I have some long running process which I want to run even if application goes in background. I am calling application's beginBackgroundTaskWithExpirationHandler: method and in the expirationBlock I am calling application's endBackgroundTask. Here is the implementation:

__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
});

In some cases, my serial queue have more task to perform which can not be completed within the time provided by system. So expiration block will execute and in that I am ending the UIBackgroundTaskIdentifier but not stopping the dispatch process (I can't even cancel a dispatch).

Apple's document says:

Each call to the beginBackgroundTaskWithName:expirationHandler: or beginBackgroundTaskWithExpirationHandler: method generates a unique token to associate with the corresponding task. When your app completes a task, it must call the endBackgroundTask: method with the corresponding token to let the system know that the task is complete. Failure to call the endBackgroundTask: method for a background task will result in the termination of your app. If you provided an expiration handler when starting the task, the system calls that handler and gives you one last chance to end the task and avoid termination.

So, according to this if I doesn't call the endBackgroundTask: my app will be terminated, which is ok.

My question is: with my current implementation what if I call endBackgroundTask: in expirationHandler block and my dispatch queue's task doesn't get complete? My app will be terminated or will be suspended?

thanks

Josefajosefina answered 28/4, 2015 at 12:29 Comment(3)
if you called endBackgroundTask: in any where your queue. it just suspended your application put the application in sleep.Careworn
@chiragshah so this can not be a cause watchdog to kill my app right? watchdog will kill the app only if I miss endBackgroundTask right?Josefajosefina
yes i do the same think in my application it can work fineCareworn
R
15

Here are some scenario, which you need to handle while using beginBackgroundTaskWithExpirationHandler otherwise your app will terminate.

Scenario 1 : Your app is running in Foreground. you are start beginBackgroundTaskWithExpirationHandler then enter in Background mode. your app keep alive for long.

Scenario 2 : Your app is running in Foreground. you are start beginBackgroundTaskWithExpirationHandler then enter in Background mode. Then come back to Foreground mode and you are not calling endBackgroundTask then your application still execute background queue so it will extent that process for next 3 minute (After IOS 7 introduce. before IOS 7, the process execution time was 10 minute). so you must need to cancel background queue and task come out from background queue and enter in foreground queue.

So here are the code that show you. what is best way handle background process.

Step 1: Declare __block UIBackgroundTaskIdentifier bgTask as global variable.

Step 2: To add following code in applicationDidEnterBackground.

- (void)applicationDidEnterBackground:(UIApplication *)application {

         bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
         bgTask = UIBackgroundTaskInvalid;
          }];

}

Step 3: Stop background task handler once apps come in foreground mode.

- (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];

}
Ream answered 29/4, 2015 at 5:45 Comment(2)
thanks for the answer. In the scenario 2, what if I don't call endBackgroundTask when the app comes to foreground? I am already calling the endBackgroundTask in expiration block.Josefajosefina
Compiler will understand that your background queue is still execute so its extent execution to next 3 minute after that compiler terminate your app. this is scenario that happen to me.Ream
S
1

If you don't call endBackgroundTask in your expiration handler, your app will be terminated.

Once you call endBackgroundTask in your expiration handler, you are only telling the OS "OK, I'm done with saving critical tasks. It's up to you now." After that your app may be suspended for a while and terminated later, or it may be terminated immediately depending on the system resources.

Subcontinent answered 29/4, 2015 at 5:57 Comment(0)
S
0

the problem of your demo code is that u lose the endBackgroundTask in your user task procedure.

you should use as below :

__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;
});
Sightless answered 5/4, 2020 at 10:8 Comment(0)
S
0

@Jatin Patel answer is good. Just change the implementation of ApplicationWillEnterForeground with below code so that your application should never terminate

DispatchQueue.global(qos: .background).async {
    // sends registration to background queue
    application.endBackgroundTask(self.bgTask)
    self.bgTask = UIBackgroundTaskIdentifier.invalid
}
Seamanship answered 1/5, 2020 at 6:47 Comment(0)
T
0

My app streams music in the background and I was getting this error.

[BackgroundTask] Background Task 1 ("Called by UIKitCore, from __47-[UIApplication _applicationDidEnterBackground]block_invoke"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(:) for your task in a timely manner to avoid this.

I fixed it using this code:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self->backgroundTask];
        self->backgroundTask = UIBackgroundTaskInvalid;
     }];
}
Turnbow answered 30/10, 2021 at 14:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.