【发布时间】:2015-03-26 16:02:19
【问题描述】:
我知道有很多关于类似主题的问题,但我认为没有人能完全解决这个问题(尽管我很高兴被证明是错误的!)。
首先是一些背景;我开发了一个在后台监控用户位置的应用程序,这工作正常,但电池使用率很高。为了尝试改进这一点,我的目标是每 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 会暂停,直到应用程序进入前台。
关于保持 NSTimers 在后台运行以及使用 beginBackgroundTaskWithExpirationHandler 的一些类似的 SO 问题,所以我在 NSTimer 启动之前将其添加到 viewDidLoad 方法中。
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 继续触发。
这是预期的行为吗?我将进行更多测试,看看这个解决方案是否长期有效,但我不禁觉得我在使用后台任务时遗漏了什么?
【问题讨论】:
标签: ios ios8 nstimer cllocationmanager