仅保留一项后台任务 [英] Keep just one background task

查看:93
本文介绍了仅保留一项后台任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个使用后台任务的应用程序,以每20秒跟踪一次用户位置.一切都很好,只不过当我在后台进入应用程序时,会创建一个新的后台任务,以便最终我可以拥有多个正在运行的后台任务. 我试图在applicationWillEnterForeground中添加[[UIApplication sharedApplication] endBackgroundTask:bgTask];,但这无济于事. 关键是,我想在进入应用程序时使所有正在运行的后台任务无效/禁用,而在后台进入时创建一个新的后台任务,或者使一个后台任务保持运行状态.

- (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
}

我建议将UIBackgroundTaskIdentifier更改为应用程序委托类的属性,并在didFinishLaunchingWithOptions中将其初始化为UIBackgroundTaskInvalid.然后,在其他应用程序委托方法中,您只需检查此属性的值即可确定是否有后台任务标识符要结束.

-

一个无关的观察,但是您不需要那个runloop东西.只需将计时器安排在主线程/运行循环上(使用scheduledTimerWithTimeInterval),然后删除所有这些运行循环内容(因为您已将其添加到主运行循环中,并且该运行循环已在运行).

例如,假设我想在后台运行应用程序时每隔10秒执行一次操作,那么我将执行以下操作:

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

现在,在您的代码示例中,该计时器不是重复计时器,因此,如果您只想触发一次计时器,则将repeats设置为NO,然后确保startTracking然后结束后台任务(如果您不打算做其他任何事情,那么让应用程序保持活动状态毫无意义).

顺便说一句,请确保您在设备上运行此代码,并且不要从Xcode运行它(因为附加到Xcode会更改应用程序的后台行为).

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 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).

这篇关于仅保留一项后台任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆