从后台任务启动 iOS 7 中的位置管理器 [英] Start Location Manager in iOS 7 from background task
问题描述
似乎在 iOS 7 中,应用程序无法再从后台任务启动位置管理器(通过调用 startUpdatingLocation).
It seems that in iOS 7 an app can not start Location Manager (by calling startUpdatingLocation) from the background task anymore.
在 iOS 6 中,我使用了此处描述的方法:https://stackoverflow.com/a/6465280 运行后台位置更新每 n 分钟.这个想法是使用计时器运行后台任务并在计时器触发时启动位置管理器.之后关闭位置管理器并启动另一个后台任务.
In iOS 6 I used approach described here: https://stackoverflow.com/a/6465280 to run background location update every n minutes. The idea was to run background task with a timer and start Location Manager when the timer triggers it. After that turn off Location Manager and start another background task.
更新到 iOS 7 后,此方法不再适用.启动位置管理器后,应用程序不会收到任何 locationManager:didUpdateLocations.有任何想法吗?
After updating to iOS 7 this approach does not work anymore. After starting Location Manager an app does not receive any locationManager:didUpdateLocations. Any ideas?
推荐答案
我找到了问题/解决方案.当需要启动位置服务和停止后台任务时,应延迟停止后台任务(我使用了 1 秒).否则定位服务将无法启动.此外,定位服务应保持开启几秒钟(在我的示例中为 3 秒).
I found the problem/solution. When it is time to start location service and stop background task, background task should be stopped with a delay (I used 1 second). Otherwise location service wont start. Also Location Service should be left ON for a couple of seconds (in my example it is 3 seconds).
另一个重要的通知,iOS 7 的最大后台时间现在是 3 分钟而不是 10 分钟.
Another important notice, max background time in iOS 7 is now 3 minutes instead of 10 minutes.
于 2016 年 10 月 29 日更新
有一个 cocoapod APScheduledLocationManager 允许每 n 秒达到所需的位置精度.
There is a cocoapod APScheduledLocationManager that allows to get background location updates every n seconds with desired location accuracy.
let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)
该存储库还包含一个用 Swift 3 编写的示例应用程序.
The repository also contains an example app written in Swift 3.
更新于 2014 年 5 月 27 日
Objective-C 示例:
Objective-C example:
1) 在.plist"文件中将 UIBackgroundModes
设置为location".
1) In ".plist" file set UIBackgroundModes
to "location".
2) 在您想要的任何位置创建 ScheduledLocationManager
的实例.
2) Create instance of ScheduledLocationManager
anywhere you want.
@property (strong, nonatomic) ScheduledLocationManager *slm;
3) 设置它
self.slm = [[ScheduledLocationManager alloc]init];
self.slm.delegate = self;
[self.slm getUserLocationWithInterval:60]; // replace this value with what you want, but it can not be higher than kMaxBGTime
4) 实现委托方法
-(void)scheduledLocationManageDidFailWithError:(NSError *)error
{
NSLog(@"Error %@",error);
}
-(void)scheduledLocationManageDidUpdateLocations:(NSArray *)locations
{
// You will receive location updates every 60 seconds (value what you set with getUserLocationWithInterval)
// and you will continue to receive location updates for 3 seconds (value of kTimeToGetLocations).
// You can gather and pick most accurate location
NSLog(@"Locations %@",locations);
}
这是 ScheduledLocationManager 的实现:
Here is implementation of ScheduledLocationManager:
ScheduledLocationManager.h
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
@protocol ScheduledLocationManagerDelegate <NSObject>
-(void)scheduledLocationManageDidFailWithError:(NSError*)error;
-(void)scheduledLocationManageDidUpdateLocations:(NSArray*)locations;
@end
@interface ScheduledLocationManager : NSObject <CLLocationManagerDelegate>
-(void)getUserLocationWithInterval:(int)interval;
@end
ScheduledLocationManager.m
#import "ScheduledLocationManager.h"
int const kMaxBGTime = 170; // 3 min - 10 seconds (as bg task is killed faster)
int const kTimeToGetLocations = 3; // time to wait for locations
@implementation ScheduledLocationManager
{
UIBackgroundTaskIdentifier bgTask;
CLLocationManager *locationManager;
NSTimer *checkLocationTimer;
int checkLocationInterval;
NSTimer *waitForLocationUpdatesTimer;
}
- (id)init
{
self = [super init];
if (self) {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
}
return self;
}
-(void)getUserLocationWithInterval:(int)interval
{
checkLocationInterval = (interval > kMaxBGTime)? kMaxBGTime : interval;
[locationManager startUpdatingLocation];
}
- (void)timerEvent:(NSTimer*)theTimer
{
[self stopCheckLocationTimer];
[locationManager startUpdatingLocation];
// in iOS 7 we need to stop background task with delay, otherwise location service won't start
[self performSelector:@selector(stopBackgroundTask) withObject:nil afterDelay:1];
}
-(void)startCheckLocationTimer
{
[self stopCheckLocationTimer];
checkLocationTimer = [NSTimer scheduledTimerWithTimeInterval:checkLocationInterval target:self selector:@selector(timerEvent:) userInfo:NULL repeats:NO];
}
-(void)stopCheckLocationTimer
{
if(checkLocationTimer){
[checkLocationTimer invalidate];
checkLocationTimer=nil;
}
}
-(void)startBackgroundTask
{
[self stopBackgroundTask];
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
//in case bg task is killed faster than expected, try to start Location Service
[self timerEvent:checkLocationTimer];
}];
}
-(void)stopBackgroundTask
{
if(bgTask!=UIBackgroundTaskInvalid){
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
-(void)stopWaitForLocationUpdatesTimer
{
if(waitForLocationUpdatesTimer){
[waitForLocationUpdatesTimer invalidate];
waitForLocationUpdatesTimer =nil;
}
}
-(void)startWaitForLocationUpdatesTimer
{
[self stopWaitForLocationUpdatesTimer];
waitForLocationUpdatesTimer = [NSTimer scheduledTimerWithTimeInterval:kTimeToGetLocations target:self selector:@selector(waitForLoactions:) userInfo:NULL repeats:NO];
}
- (void)waitForLoactions:(NSTimer*)theTimer
{
[self stopWaitForLocationUpdatesTimer];
if(([[UIApplication sharedApplication ]applicationState]==UIApplicationStateBackground ||
[[UIApplication sharedApplication ]applicationState]==UIApplicationStateInactive) &&
bgTask==UIBackgroundTaskInvalid){
[self startBackgroundTask];
}
[self startCheckLocationTimer];
[locationManager stopUpdatingLocation];
}
#pragma mark - CLLocationManagerDelegate methods
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
if(checkLocationTimer){
//sometimes it happens that location manager does not stop even after stopUpdationLocations
return;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(scheduledLocationManageDidUpdateLocations:)]) {
[self.delegate scheduledLocationManageDidUpdateLocations:locations];
}
if(waitForLocationUpdatesTimer==nil){
[self startWaitForLocationUpdatesTimer];
}
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if (self.delegate && [self.delegate respondsToSelector:@selector(scheduledLocationManageDidFailWithError:)]) {
[self.delegate scheduledLocationManageDidFailWithError:error];
}
}
#pragma mark - UIAplicatin notifications
- (void)applicationDidEnterBackground:(NSNotification *) notification
{
if([self isLocationServiceAvailable]==YES){
[self startBackgroundTask];
}
}
- (void)applicationDidBecomeActive:(NSNotification *) notification
{
[self stopBackgroundTask];
if([self isLocationServiceAvailable]==NO){
NSError *error = [NSError errorWithDomain:@"your.domain" code:1 userInfo:[NSDictionary dictionaryWithObject:@"Authorization status denied" forKey:NSLocalizedDescriptionKey]];
if (self.delegate && [self.delegate respondsToSelector:@selector(scheduledLocationManageDidFailWithError:)]) {
[self.delegate scheduledLocationManageDidFailWithError:error];
}
}
}
#pragma mark - Helpers
-(BOOL)isLocationServiceAvailable
{
if([CLLocationManager locationServicesEnabled]==NO ||
[CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied ||
[CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted){
return NO;
}else{
return YES;
}
}
@end
这篇关于从后台任务启动 iOS 7 中的位置管理器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!