Keep just one background task

一个人想着一个人 提交于 2019-12-08 07:19:30

问题


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
}

回答1:


I would suggest changing UIBackgroundTaskIdentifier to be a property of the app delegate class and initialize it to UIBackgroundTaskInvalid in didFinishLaunchingWithOptions. 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:

@interface AppDelegate ()

@property (atomic) UIBackgroundTaskIdentifier bgTask;
@property (nonatomic, weak) NSTimer *timer;

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.bgTask = UIBackgroundTaskInvalid;
    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        if (self.bgTask != UIBackgroundTaskInvalid) {
            [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
            self.bgTask = UIBackgroundTaskInvalid;
        }
    }];

    self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(startTracking) userInfo:nil repeats:YES];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // invalidate the timer if still running

    [self.timer invalidate];

    // end the background task if still present

    if (self.bgTask != UIBackgroundTaskInvalid) {
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }
}

- (void)startTracking{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

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 to NO, but then make sure that startTracking 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).




回答2:


Specify location background mode Use an NSTimer in the background by using UIApplication:beginBackgroundTaskWithExpirationHandler: In case n is smaller than UIApplication:backgroundTimeRemaining ,it will work just fine, in case n is larger, the location 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.



来源:https://stackoverflow.com/questions/33786515/keep-just-one-background-task

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!