我知道在类似的话题上有很多问题,但我认为没有人能解决这个问题(尽管我很高兴被证明是错的!)
首先是一些背景,我已经开发了一个应用程序来监控用户在后台的位置,这是很好的工作,但电池使用率很高。为了改进这一点,我的目标是每隔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
的使用,因此我在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将继续运行。
这是预期的行为吗?我将运行更多的测试,看看这个解决方案在长期内是否有效,但我不禁觉得我在使用后台任务时遗漏了什么?
发布于 2015-03-27 12:31:42
有一个选项可以将后台任务用于长期运行的任务,然后iOS不支持执行。
根据后台执行文档,要使用它,应该将UIBackgroundModes键添加到Info.plist中,并带有值位置(用于位置更新)。还有另一个原因:
实现长期运行的任务。
实现这些服务的应用程序必须声明它们支持的服务,并使用系统框架来实现这些服务的相关方面。声明服务可以让系统知道您使用的是哪些服务,但在某些情况下,实际上是系统框架阻止您的应用程序被挂起。
可能只需初始化CLLocationManager即可使后台任务不被挂起。
https://stackoverflow.com/questions/29283328
复制相似问题