问题
I want to write application that every 5 min sends to server low-accuracy-location for its both modes: foreground and background. Because I don't want to reduce the battery charge and the locationSignificantChange gives only the high-accuracy-values, I start/stop locationUpdates from GPS every 5 min. Application works fine from foreground mode, but works only about 1 hour from background mode (and stops to send location afterwards). I guess I am missing something in backgroundTask/NSTimer code because I am new with iOS. I will very appreciate your help. The application will be only for iOS 7 and up.
In general my algorithm is follow:
** start backGroundTask
** [_locationManager startUpdatingLocation]
** handle received location in "didUpdateLocations:" listener
** [_locationManager stopUpdatingLocation]
** create new thread and fire the NSTimer with delay 5 min
** end backGroundTask
This is my code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self startLocationManager];
// .. other init app code
}
- (void) startLocationManager {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
_locationManager.distanceFilter = 10;
_locationManager.delegate = self;
_locationManager.pausesLocationUpdatesAutomatically = NO;
[_locationManager startLocation];
}
- (void) startLocation {
UIApplication * app = [UIApplication sharedApplication];
self.bkgdTask = [app beginBackgroundTaskWithExpirationHandler:^{
}];
[self.locationManager startUpdatingLocation];
}
- (void) stopUpdatingLocation {
[_locationManager stopUpdatingLocation];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
self.locationNextTimer = [NSTimer scheduledTimerWithTimeInterval:5 * 60
target:self
selector:@selector(startLocation)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:self.locationNextTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
[app endBackgroundTask:self.bkgdTask];
self.bkgdTask = UIBackgroundTaskInvalid;
});
}
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
[self.locationNextTimer invalidate];
self.locationNextTimer = nil;
//ToDo Location Handling
[self stopUpdatingLocation];
}
EDIT:
As I understood from iOS7 Apple wants that startUpdatingLocation for locationManager will be done only from foreground. So any ideas for solution of this problem? I also tried additional solution: instead stop/startUpdatingLocation with NSTimer, to change the accuracy and distanceFilter for big/small values with NSTimer. It did not work because I receive trigger in "didUpdateLocations:" listener 4 times for "freeway drive" instead of one time.
回答1:
The location manager will decide for itself the least activity based on your settings. Set the parameters as appropriate for your use case then act responsibly in your delegate. Test the locations received, if they are too recent then you do nothing. This will allow your app processing to impact battery as little as necessary.
significantLocationChanges
is meant to reduce impact and, in my experience, does a pretty good job of it. Another option is to use region monitoring instead or along with.
You can further limit activity by creating regions of a size based on the accuracy you wish to achieve. Update regions and save location only as boundaries are crossed. This may be more appropriate for your needs.
回答2:
To get location update when the app is in the background versus when the app is suspended are 2 very different scenario. You will have to handle them differently.
To get the location update when
A) The app is In the background, please see: Background Location Services not working in iOS 7
B) The app is suspended/terminated, please see: How to Get Location Updates for iOS 7 and 8 Even when the App is Suspended
The source codes for the above 2 scenario are available on GitHub.
来源:https://stackoverflow.com/questions/21220245/location-update-in-background-and-suspended-app-modes