Ios 只保留一个后台任务

Ios 只保留一个后台任务,ios,uibackgroundtask,Ios,Uibackgroundtask,我正在开发一个应用程序,它使用后台任务每20秒跟踪一次用户的位置。一切都很好,只是当我在后台进入应用程序时,会创建一个新的后台任务,这样我就可以在最后运行多个后台任务。 我尝试添加[[UIApplication sharedApplication]endBackgroundTask:bgTask]将进入前台,但这不起任何作用。 关键是,我想在进入应用程序时使所有正在运行的后台任务失效/禁用,并在进入后台时创建一个新的后台任务,或者只运行一个后台任务 - (void)applicationDidE

我正在开发一个应用程序,它使用后台任务每20秒跟踪一次用户的位置。一切都很好,只是当我在后台进入应用程序时,会创建一个新的后台任务,这样我就可以在最后运行多个后台任务。 我尝试添加
[[UIApplication sharedApplication]endBackgroundTask:bgTask]应用程序中的code>将进入前台
,但这不起任何作用。 关键是,我想在进入应用程序时使所有正在运行的后台任务失效/禁用,并在进入后台时创建一个新的后台任务,或者只运行一个后台任务

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

指定位置背景模式 通过使用
UIApplication:beginBackgroundTaskWithExpirationHandler:
如果
n
小于UIApplication:backgroundTimeRemaining,它将正常工作,如果
n
大于UIApplication:backgroundTimeRemaining,则应在没有剩余时间之前再次启用(并禁用)位置管理器,以避免后台任务被终止

这是可行的,因为位置是三种允许的后台执行类型之一


注意:通过在模拟器中测试此功能,在手机上可以正常工作。我建议将
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会改变应用程序的后台行为)。

对于我的情况,
currentRunLoop
显然是
mainRunLoop
,不是吗?你说不需要添加计时器,事实上,我必须使用计时器使应用程序在后台运行180秒以上。不,我是说,
scheduledTimerWithTimeInterval
已经在当前运行循环中启动计时器。因此,您不必将其添加到运行循环中。您也不必
运行该runloop(它已经在运行)。您必须手动实例化计时器并将其添加到运行循环的唯一时间是在当前运行循环以外的运行循环上执行此操作时。我正在测试您的观察结果,我只是实例化了
scheduledTimerWithTimeInterval
,而没有将其添加到
currentRunLoop
,并且从未调用计时器。如果您是弱计时器,当您计划一个重复计时器时,它将为您保留,直到它失效(或者,如果不重复,则直到它触发)。因此,您不需要对它进行强引用(并且当它无效时,您希望它
nil
-ed)。重新原子访问
bgTask
我这样做是出于谨慎,因为我不完全确定过期处理程序是否保证在主线程上运行(可能是,但我不确定)。您可能不需要将其原子化,但我只是小心了一点(开销可以忽略不计)。@androniennn如果您担心功耗(我们都应该担心),那么使用标准位置服务和后台执行是一个更大的问题。除非您绝对需要细粒度的位置服务,否则一般建议使用重大更改服务进行后台位置更新。顺便说一句,如果您想在后台跟踪用户的位置,每20秒使用一次计时器是一种非常耗电的方式。一般来说,人们会使用“重大更改”服务(该服务在后台持续运行,绕过
UIBackgroundTaskIdentifier
的3分钟限制,但也使用低功耗的定位服务)。@Rob:我需要跟踪用户的位置,从而跟踪速度,以了解他是否在车内。好的,重要的位置更改可以工作,但是在重要位置执行
didUpdateLocations
委派方法之前,不会检测到他的第一部分行程(大约3分钟)。哦,如果您试图跟踪车速,那么重要的更改服务将不够准确。不幸的是,你必须使用标准的定位服务。但是如果用户在他或她的车里,他们大概可以使用电源适配器,因此在这种特殊情况下,用户可能可以接受功耗。没错!你们都说了!电源适配器对于此类应用(跟踪)非常有用,我只需要优化空闲时的电池消耗。这就是为什么我要在后台优化每一行代码。