I'm developing an application that uses a background task to keep tracking of the user position every 20 seconds. All is fine, except that when I enter the application in background, a new background tasks is created, so that I can have in final multiple background tasks that are running.
I tried to add [[UIApplication sharedApplication] endBackgroundTask:bgTask];
in applicationWillEnterForeground
, but that do nothing.
The point is that I want to invalidate/disable all running background tasks when I enter the app and create a new one when I enter in background, or to keep a just one background task running.
- (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
}
I would suggest changing
UIBackgroundTaskIdentifier
to be a property of the app delegate class and initialize it toUIBackgroundTaskInvalid
indidFinishLaunchingWithOptions
. Then, in your other app delegate methods, you can just check the value of this property to determine whether there is a background task identifier to end or not.--
An unrelated observation, but you don't need that runloop stuff. Just schedule the timer on the main thread/runloop (with
scheduledTimerWithTimeInterval
) and get rid of all of that runloop stuff (because you already added it to the main runloop and that runloop is already running).For example, let's assume I wanted to do something every 10 seconds while the app was in background, I'd do something like the following:
Now, in your code sample, the timer wasn't a repeating timer, so if you only wanted to fire the timer once, then set
repeats
toNO
, but then make sure thatstartTracking
then ended the background task (no point in keeping the app alive if you're not going to do anything else).BTW, make sure you run this code on a device, and do not run it from Xcode (as being attached to Xcode changes the background behavior of apps).
Specify location background mode Use an
NSTimer
in the background by usingUIApplication:beginBackgroundTaskWithExpirationHandler:
In casen
is smaller than UIApplication:backgroundTimeRemaining ,it will work just fine, in casen
is larger, thelocation manager
should be enabled (and disabled) again before there is no time remaining to avoid the background task being killed.This does work since location is one of the three allowed types of background execution.
Note: Did loose some time by testing this in the simulator where it doesn't work, works fine on phone.