我知道有很多关于类似主题的问题,但是我认为没有一个问题能解决这个问题(尽管我很高兴被证明是错误的!)。

首先是一些背景;我开发了一个可在后台监视用户位置的应用程序,它运行正常,但电池使用率很高。为了尝试改善此问题,我的目标是每x分钟“唤醒”,致电startUpdatingLocation获取职位,然后致电stopUpdatingLocation

考虑以下我为测试而汇总的示例:

@interface ViewController ()
@property (strong, nonatomic) NSTimer *backgroundTimer;
@property (strong, nonatomic) CLLocationManager *locationManager;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Add observers to monitor background / foreground transitions
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];

    _locationManager = [[CLLocationManager alloc] init];
    [_locationManager requestAlwaysAuthorization];
    _locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
    _locationManager.delegate = self;
    _locationManager.distanceFilter=100;
    [_locationManager startUpdatingLocation];


    NSTimeInterval time = 60.0;
    _backgroundTimer =[NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startLocationManager) userInfo:nil repeats:YES];

}

-(void)applicationEnterBackground{
    NSLog(@"Entering background");
}

-(void)applicationEnterForeground{
    NSLog(@"Entering foreground");
}

-(void)startLocationManager{
    NSLog(@"Timer fired, starting location update");
    [_locationManager startUpdatingLocation];
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
    NSLog(@"New location received");
    [_locationManager stopUpdatingLocation];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
@end

不出所料,当应用程序进入后台时,CLLocationManager不会更新位置,因此NSTimer会暂停,直到应用程序进入前台为止。

一些类似的SO问题询问了有关如何使NSTimers在后台运行以及是否使用beginBackgroundTaskWithExpirationHandler的问题,所以我在viewDidLoad开始之前将其添加到NSTimer方法中。
UIBackgroundTaskIdentifier bgTask = 0;
UIApplication *app = [UIApplication sharedApplication];
NSLog(@"Beginning background task");
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    NSLog(@"Background task expired");
    [app endBackgroundTask:bgTask];
}];
NSLog(@"bgTask=%d", bgTask);

通过短期测试(是否可以长期运行尚待证明),这似乎可以解决问题,但我不明白为什么。

如果查看下面的日志输出,可以看到调用beginBackgroundTaskWithExpirationHandler达到了预期的目标,因为当应用程序进入后台时NSTimer继续触发:
2015-03-26 15:45:38.643 TestBackgroundLocation[1046:1557637] Beginning background task
2015-03-26 15:45:38.645 TestBackgroundLocation[1046:1557637] bgTask=1
2015-03-26 15:45:38.703 TestBackgroundLocation[1046:1557637] New location received
2015-03-26 15:45:46.459 TestBackgroundLocation[1046:1557637] Entering background
2015-03-26 15:46:38.682 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
2015-03-26 15:46:38.697 TestBackgroundLocation[1046:1557637] New location received
2015-03-26 15:47:38.666 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
2015-03-26 15:47:38.677 TestBackgroundLocation[1046:1557637] New location received
2015-03-26 15:48:38.690 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
2015-03-26 15:48:38.705 TestBackgroundLocation[1046:1557637] New location received
2015-03-26 15:48:42.357 TestBackgroundLocation[1046:1557637] Background task expired
2015-03-26 15:49:38.733 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
2015-03-26 15:49:38.748 TestBackgroundLocation[1046:1557637] New location received
2015-03-26 15:50:38.721 TestBackgroundLocation[1046:1557637] Timer fired, starting location update
2015-03-26 15:50:38.735 TestBackgroundLocation[1046:1557637] New location received
2015-03-26 15:50:44.361 TestBackgroundLocation[1046:1557637] Entering foreground

由此,您可以看到后台任务在预期的大约3分钟后过期,但是NSTimer在此之后继续触发。

这是预期的行为吗?我将运行更多测试以查看该解决方案是否可以长期运行,但是我不禁觉得使用后台任务会丢失某些东西?

最佳答案

经过大量测试,结果证明NSTimer不会在任务到期后无限期运行。在某个时间点(可以是几个小时,可以立即),计时器停止点火。

最后,解决我的问题的方法是在不需要监视位置的时候降低GPS精度并增加距离过滤器,如下所述:

Periodic iOS background location updates

关于ios - 为什么beginBackgroundTaskWithExpirationHandler允许NSTimer在后台运行?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29283328/

10-13 07:31