从后台任务iOS 7中启动位置pipe理器
看起来,在iOS 7中,应用程序无法从后台任务中启动位置pipe理器(通过调用startUpdatingLocation)。
在iOS 6中,我使用了这里描述的方法: https : //stackoverflow.com/a/6465280每n分钟运行一次后台位置更新。 这个想法是用定时器运行后台任务,并在计时器触发时启动位置pipe理器。 之后,closures位置pipe理器并开始另一个后台任务。
更新到iOS 7后,此方法不再适用。 启动位置pipe理器后,应用程序不会收到任何locationManager:didUpdateLocations。 有任何想法吗?
我find了问题/解决scheme。 当开始定位服务并停止后台任务时,应该延迟后台任务(我用了1秒)。 否则位置服务将无法启动。 位置服务也应该保持ON几秒钟(在我的例子中是3秒)。
另一个重要的通知,在iOS 7的最大背景时间现在是3分钟,而不是10分钟。
16年10月29日更新
有一个cocoapod APScheduledLocationManager ,允许每n秒获得所需的位置准确性的背景位置更新。
let manager = APScheduledLocationManager(delegate: self) manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)
该库还包含一个用Swift 3编写的示例应用程序。
更新于2014年5月27日
Objective-C示例:
1)在“.plist”文件中将UIBackgroundModes
设置为“location”。
2)在任何你想要的地方创buildScheduledLocationManager
实例。
@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的实现:
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
我尝试了你的方法,但是在我这边却不行。 你能告诉我你的代码吗?
我实际上find了解决iOS 7中的位置服务问题的解决scheme。
在iOS 7中,您无法在后台启动位置服务。 如果您希望位置服务在后台继续运行,则必须在前台启动它,并将继续在后台运行。
如果你像我一样,停止位置服务,并使用计时器重新启动它在后台,它不会在iOS 7中工作。
有关更详细的信息,您可以观看WWDC 2013的前8分钟video307: https : //developer.apple.com/wwdc/videos/
更新:位置服务也可以在后台工作 。 请检查后台定位服务不适用于iOS 7中的更新后的post,并在Github上发布完整的解决scheme,并解释详细信息。
实现这个的步骤如下:
-
在项目的info.plist的“所需的背景模式”的项目0处添加“位置更新的应用寄存器”。
-
在应用程序下面写下代码完成启动。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startFetchingLocationsContinously) name:START_FETCH_LOCATION object:nil];
-
从下面的代码中写下你想要开始跟踪的代码
[[NSNotificationCenter defaultCenter] postNotificationName:START_FETCH_LOCATION object:nil]; AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate]; [appDelegate startUpdatingDataBase];
-
将以下代码粘贴到AppDelegate.m
#pragma mark - Location Update -(void)startFetchingLocationsContinously{ NSLog(@"start Fetching Locations"); self.locationUtil = [[LocationUtil alloc] init]; [self.locationUtil setDelegate:self]; [self.locationUtil startLocationManager]; } -(void)locationRecievedSuccesfullyWithNewLocation:(CLLocation*)newLocation oldLocation:(CLLocation*)oldLocation{ NSLog(@"location received successfullly in app delegate for Laitude: %f and Longitude:%f, and Altitude:%f, and Vertical Accuracy: %f",newLocation.coordinate.latitude,newLocation.coordinate.longitude,newLocation.altitude,newLocation.verticalAccuracy); } -(void)startUpdatingDataBase{ UIApplication* app = [UIApplication sharedApplication]; bgTask = UIBackgroundTaskInvalid; bgTask = [app beginBackgroundTaskWithExpirationHandler:^(void){ [app endBackgroundTask:bgTask]; }]; SAVE_LOCATION_TIMER = [NSTimer scheduledTimerWithTimeInterval:300 target:self selector:@selector(startFetchingLocationsContinously) userInfo:nil repeats:YES]; }
-
按名称“LocationUtil”添加一个类,并将以下代码粘贴到头文件中:
#import <Foundation/Foundation.h> #import <CoreLocation/CoreLocation.h> @protocol LocationRecievedSuccessfully <NSObject> @optional -(void)locationRecievedSuccesfullyWithNewLocation:(CLLocation*)newLocation oldLocation:(CLLocation*)oldLocation; -(void)addressParsedSuccessfully:(id)address; @end @interface LocationUtil : NSObject <CLLocationManagerDelegate> { } //Properties @property (nonatomic,strong) id<LocationRecievedSuccessfully> delegate; -(void)startLocationManager;
并在LocationUtil.m中粘贴以下代码
-(void)startLocationManager{ locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; [locationManager setPausesLocationUpdatesAutomatically:YES]; //Utkarsh 20sep2013 //[locationManager setActivityType:CLActivityTypeFitness]; locationManager.distanceFilter = kCLDistanceFilterNone; locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation; [locationManager startUpdatingLocation]; //Reverse Geocoding. geoCoder=[[CLGeocoder alloc] init]; //set default values for reverse geo coding. } //for iOS<6 - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { //call delegate Method [delegate locationRecievedSuccesfullyWithNewLocation:newLocation oldLocation:oldLocation]; NSLog(@"did Update Location"); } //for iOS>=6. - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations { CLLocation *newLocation = [locations objectAtIndex:0]; CLLocation *oldLocation = [locations objectAtIndex:0]; [delegate locationRecievedSuccesfullyWithNewLocation:newLocation oldLocation:oldLocation]; NSLog(@"did Update Locationsssssss"); }