3

My app sometimes crashes when in background and shows the following crash log:

Nov  7 12:33:31 iPad backboardd[29] <Warning>: MyApp[3096] has active assertions beyond permitted time: 
    {(
        <BKProcessAssertion: 0x14680c60> identifier: Called by MyApp, from -[AppDelegate applicationDidEnterBackground:] process: MyApp[3096] permittedBackgroundDuration: 180.000000 reason: finishTask owner pid:3096 preventSuspend  preventIdleSleep  preventSuspendOnSleep 
    )}

Looking through other questions I found out that the crash message indicates that I didn't end a task properly, so when its time expired the OS ended it and crashed my app.

So I added some NSLogs:

- (void)applicationDidEnterBackground:(UIApplication *)application
{       
    [self saveContext];
    [Settings setCallLock:YES];

    [self saveStuff];

    if ([self isBackgroundTaskNeeded]) 
    {

        UIApplication*  app = [UIApplication sharedApplication];

        bgTask = [app beginBackgroundTaskWithExpirationHandler:^{

            [self pauseDownloads];

            NSLog(@"Debug - Ending background task %d",bgTask);
            [app endBackgroundTask:bgTask];
            NSLog(@"Debug -  Background task %d ended",bgTask);
            bgTask = UIBackgroundTaskInvalid;


        }];
        NSLog(@"Debug - Starting background task %d",bgTask);
        [self initBackground];

    }

}

and found out that the task was called twice when it crashed:

Nov  7 12:30:02 iPad MyApp[3096] <Warning>: Debug - Starting background task 7
Nov  7 12:30:30 iPad MyApp[3096] <Warning>: Debug - Starting background task 8
Nov  7 12:33:26 iPad MyApp[3096] <Warning>: Debug - Ending background task 8
Nov  7 12:33:26 iPad MyApp[3096] <Warning>: Debug -  Background task 8 ended
Nov  7 12:33:26 iPad MyApp[3096] <Warning>: Debug - Ending background task 0
Nov  7 12:33:26 iPad MyApp[3096] <Warning>: Debug -  Background task 0 ended

I wasn't able to tell when the double call happens and why. So question is why is the task being called twice and is there a way to avoid it?

Edit:

- (void) pauseDownloads
{
    for (AFDownloadRequestOperation *operation in self.operationQueue.operations)
    {
        if(![operation isPaused])
        {
            [operation pause];
        }
    }
}
Community
  • 1
  • 1
Leonardo
  • 1,740
  • 18
  • 27
  • can you post pauseDownloads method? – sash Nov 07 '13 at 15:21
  • Check your initBackground method, and end background task after you have finished downloading. In iOS 7 max background task is 3 min, in iOS 6 around 10 min. – sash Nov 07 '13 at 16:15

2 Answers2

6

You should endBackground without expiration..

    UIApplication*  app = [UIApplication sharedApplication];

    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{

        [self pauseDownloads];

        NSLog(@"Debug - Ending background task %d",bgTask);
        [app endBackgroundTask:bgTask];
        NSLog(@"Debug -  Background task %d ended",bgTask);
        bgTask = UIBackgroundTaskInvalid;


    }];
    NSLog(@"Debug - Starting background task %d",bgTask);

    [self initBackground];

    NSLog(@"Debug - Ending background task %d without expiration",bgTask);
    [app endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;

When app enters in foreground, put this balancing endBackgroundTask call -

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    if (bgTask != UIBackgroundTaskInvalid) {
       NSLog(@"Debug - Ending background task %d without expiration (when applicationWillEnterForeground)",bgTask);
       [app endBackgroundTask:bgTask];
       bgTask = UIBackgroundTaskInvalid;
    }

    // Also, to counter expiration handler pauseDownloads call, consider resuming the downloads here (or applicationDidBecomeActive app delegate).
}

Because - The expiration handler block {} is executed when the background task expire (usually up to 10 min, but not guaranteed). I assume your actual background task is [self initBackground];, otherwise you should put it outside the block and before endBackgroundTask call.

(Another potential issue)

Also, I noticed your pauseDownloads code now, it seems you are performing downloads using NSOperationQueue when app goes in background. In such a case, ensure that the endBackgroundTask is not called until you have finished downloading or expiration handler is executed. In other words, the control doesn't return too early which means you might need to monitor these download NSOperation(s).

Ashok
  • 6,224
  • 2
  • 37
  • 55
  • Actually this code wasn't written by me, I was assigned to fix this crash and the person who wrote it doesn't work here anymore, so I'm not really sure what he intended, but I believe the task wasn't ended to keep the downloads running as long as possible – Leonardo Nov 07 '13 at 15:59
  • Ok, but it's an interesting issue to fix. I believe you need at least few hours to understand and fix it, not just minutes. I would recommend you should try putting some timestamps `[NSDate date]` in these NSLogs to help you debug and understand. Note - AFAIK, each NSOperation is forked to be executed in a separate thread, provided the NSOperationQueue has not restricted it. When you are free, you may read this link - http://stackoverflow.com/questions/19569244/nsoperation-and-nsoperationqueue-working-thread-vs-main-thread/19674292#19674292 – Ashok Nov 07 '13 at 16:12
  • I'll try that, thanks, and about the task being called twice any ideas? – Leonardo Nov 07 '13 at 16:26
  • I just noticed debugging timestamps in your log at beginning. Do one thing, put a balancing `endBackgroundTask` call in `applicationWillEnterForeground` delegate method. It looks like this crash only happens when app frequently switch from foreground to background !!?? – Ashok Nov 07 '13 at 16:34
  • Yes, that's how the crash happens, when the app is frequently switched from foreground to background, I tried your suggestions and I need to run more tests, but seems to have fixed the problem thanks a lot – Leonardo Nov 07 '13 at 17:47
  • No problem, please feel free to ask further doubts. This solution might not fix all issues, depending on scenario. – Ashok Nov 07 '13 at 20:43
0

The short answer is that your crash was happening because you created two background tasks: 7 & 8 but only ended 8.

With that knowledge, you should apply Ashok's solution to ensure you always end any background task that you create.

Biosopher
  • 536
  • 4
  • 12