我正在尝试在我的应用处于非事件状态时获取位置更新(用户关闭了应用)。
2 次位置更新后,位置更新停止启动我的应用程序。
这方面的指标是我的应用中位置服务设置中的灰色箭头。
我正在尝试的是 startMonitoringSignificantLocationChanges 和 regionMonitoring 的组合。
- 我在 iPhone 4 iOS 7.1.1 上进行了测试,位置更新在 2 次更新后停止。
- 我在 iPad mini WiFi+Cellular iOS 7.1.1 中进行了测试,位置更新在 1 次更新后停止,区域监控仅发送 1 个位置。
我哪里错了?
我的代码:
AppDelegate.m:
- (BOOL)applicationUIApplication *)application didFinishLaunchingWithOptionsNSDictionary *)launchOptions
{
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
[RegionMonitoringService sharedInstance].launchOptions = launchOptions;
[[RegionMonitoringService sharedInstance] stopMonitoringAllRegions];
if ( [CLLocationManager significantLocationChangeMonitoringAvailable] ) {
[[RegionMonitoringService sharedInstance] startMonitoringSignificantLocationChanges];
} else {
NSLog(@"Significant location change service not available.");
}
if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {
[self application:application handleNewLocationEvet:launchOptions]; // Handle new location event
UIViewController *controller = [[UIViewController alloc] init];
controller.view.frame = [UIScreen mainScreen].bounds;
UINavigationController *nvc = [[UINavigationController alloc] initWithRootViewController:controller];
dispatch_async(dispatch_get_main_queue(), ^{
appDelegate.window.rootViewController = nvc;
[appDelegate.window makeKeyAndVisible];
});
}
else {
// ...
}
return YES;
}
- (void)applicationDidEnterBackgroundUIApplication *)application
{
[defaults synchronize];
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
if ([RegionMonitoringService sharedInstance].launchOptions[UIApplicationLaunchOptionsLocationKey]) {
return;
}
}
- (void)applicationUIApplication *)application handleNewLocationEvetNSDictionary *)launchOptions
{
NSLog(@"%s, launchOptions: %@", __PRETTY_FUNCTION__, launchOptions);
if (![launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) return;
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) return;
SendLocalPushNotification(@"handleNewLocationEvet");
}
RegionMonitoringService.h:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import "ServerApiManager.h"
@interface RegionMonitoringService : NSObject
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSDictionary *launchOptions;
@property (strong, nonatomic) NSDate *oldDate;
@property (strong, nonatomic) CLLocation *oldLocation;
+ (RegionMonitoringService *)sharedInstance;
- (void)startMonitoringForRegionCLRegion *)region;
- (void)startMonitoringRegionWithCoordinateCLLocationCoordinate2D)coordinate andRadiusCLLocationDirection)radius;
- (void)stopMonitoringAllRegions;
- (void)startMonitoringSignificantLocationChanges;
- (void)stopMonitoringSignificantLocationChanges;
FOUNDATION_EXPORT NSString *NSStringFromCLRegionState(CLRegionState state);
@end
RegionMonitoringService.m:
#import "RegionMonitoringService.h"
static CLLocationDistance const kFixedRadius = 250.0;
@interface RegionMonitoringService () <CLLocationManagerDelegate>
- (NSString *)identifierForCoordinateCLLocationCoordinate2D)coordinate;
- (CLLocationDistance)getFixRadiusCLLocationDistance)radius;
- (void)sortLastLocation:(CLLocation *)lastLocation;
@end
@implementation RegionMonitoringService
+ (RegionMonitoringService *)sharedInstance
{
static RegionMonitoringService *_sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (instancetype)init
{
self = [super init];
if (!self) {
return nil;
}
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_locationManager.distanceFilter = kCLDistanceFilterNone;
// _locationManager.activityType = CLActivityTypeFitness;
_locationManager.delegate = self;
return self;
}
- (void)startMonitoringForRegion:(CLRegion *)region
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[_locationManager startMonitoringForRegion:region];
}
- (void)startMonitoringRegionWithCoordinate:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDirection)radius
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (![CLLocationManager regionMonitoringAvailable]) {
NSLog(@"Warning: Region monitoring not supported on this device.");
return;
}
if (__iOS_6_And_Heigher) {
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:coordinate
radius:radius
identifier:[self identifierForCoordinate:coordinate]];
[_locationManager startMonitoringForRegion:region];
}
else {
CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:coordinate
radius:radius
identifier:[self identifierForCoordinate:coordinate]];
[_locationManager startMonitoringForRegion:region];
}
SendLocalPushNotification([NSString stringWithFormat"StartMonitor: {%f, %f}", coordinate.latitude, coordinate.longitude]);
}
- (void)stopMonitoringAllRegions
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (_locationManager.monitoredRegions.allObjects.count > 1) {
for (int i=0; i<_locationManager.monitoredRegions.allObjects.count; i++) {
if (i == 0) {
NSLog(@"stop monitor region at index %d", i);
CLRegion *region = (CLRegion *)_locationManager.monitoredRegions.allObjects[i];
[_locationManager stopMonitoringForRegion:region];
}
}
}
}
- (void)startMonitoringSignificantLocationChanges
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[_locationManager startMonitoringSignificantLocationChanges];
}
- (void)stopMonitoringSignificantLocationChanges
{
NSLog(@"%s", __PRETTY_FUNCTION__);
[_locationManager stopMonitoringSignificantLocationChanges];
}
- (NSString *)identifierForCoordinate:(CLLocationCoordinate2D)coordinate
{
NSLog(@"%s", __PRETTY_FUNCTION__);
return [NSString stringWithFormat"{%f, %f}", coordinate.latitude, coordinate.longitude];
}
FOUNDATION_EXPORT NSString *NSStringFromCLRegionState(CLRegionState state)
{
NSLog(@"%s", __PRETTY_FUNCTION__);
if (__iOS_6_And_Heigher) {
return @"Support only iOS 7 and later.";
}
if (state == CLRegionStateUnknown) {
return @"CLRegionStateUnknown";
} else if (state == CLRegionStateInside) {
return @"CLRegionStateInside";
} else if (state == CLRegionStateOutside) {
return @"CLRegionStateOutside";
} else {
return [NSString stringWithFormat"Undeterminded CLRegionState"];
}
}
- (CLLocationDistance)getFixRadius:(CLLocationDistance)radius
{
if (radius > _locationManager.maximumRegionMonitoringDistance) {
radius = _locationManager.maximumRegionMonitoringDistance;
}
return radius;
}
- (void)sortLastLocation:(CLLocation *)lastLocation
{
NSLog(@"%s, %@", __PRETTY_FUNCTION__, lastLocation);
self.oldDate = lastLocation.timestamp; // Get new date
NSTimeInterval seconds = fabs([self.oldLocation.timestamp timeIntervalSinceDate:self.oldDate]); // Calculate how seconds passed
NSInteger minutes = seconds * 60; // Calculate how minutes passed
if (lastLocation && self.oldLocation) { // New & old location are good
if ([lastLocation distanceFromLocation:self.oldLocation] >= 200 || minutes >= 30) { // Distance > 200 or 30 minutes passed
[[ServerApiManager sharedInstance] saveLocation:lastLocation]; // Send location to server
}
}
else { // We just starting location updates
[[ServerApiManager sharedInstance] saveLocation:lastLocation]; // Send new location to server
}
self.oldLocation = lastLocation; // Set old location
}
#pragma mark - CLLocationManagerDelegate Methods
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(@"%s, %@", __PRETTY_FUNCTION__, locations);
CLLocation *lastLocation = (CLLocation *)locations.lastObject;
CLLocationCoordinate2D coordinate = lastLocation.coordinate;
if (lastLocation == nil || coordinate.latitude == 0.0 || coordinate.longitude == 0.0) {
return;
}
[self startMonitoringRegionWithCoordinate:coordinate andRadius:[self getFixRadius:kFixedRadius]];
[self sortLastLocation:lastLocation];
}
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
NSLog(@"%s, currentLocation: %@, regionState: %@, region: %@",
__PRETTY_FUNCTION__, manager.location, NSStringFromCLRegionState(state), region);
}
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region
{
NSLog(@"%s, REGION: %@", __PRETTY_FUNCTION__, region);
[manager requestStateForRegion:region];
}
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
NSLog(@"%s, REGION: %@", __PRETTY_FUNCTION__, region);
[self stopMonitoringAllRegions];
[self startMonitoringRegionWithCoordinate:manager.location.coordinate andRadius:[self getFixRadius:kFixedRadius]];
CLLocation *lastLocation = manager.location;
CLLocationCoordinate2D coordinate = lastLocation.coordinate;
if (lastLocation == nil || coordinate.latitude == 0.0 || coordinate.longitude == 0.0) {
return;
}
[self sortLastLocation:manager.location];
}
@end
编辑 1:
经过几次测试后,我用多种设备(iPhone 5s、iPad mini、iPhone 4)对汽车进行了很多实时测试,结果如下:
- 在一种情况下,iPad mini 和 iPhone 4 会在应用未运行几分钟后停止更新位置,并且小箭头变为灰色。
- 当 WiFi 关闭时,准确性很差,位置更新很少。
编辑 2:
好的,经过大量的驾驶和四处走动并对其进行测试,到目前为止,它的工作原理就像一个魅力。
我设法使它工作,结合 significantLocationChanges 和区域监控,总是在我当前位置周围注册一个地理围栏,并总是在新的 UIApplicationLaunchOptionsLocationKey 到来时开始重大的位置更改。
请注意,关闭 wifi 会使准确性非常低,甚至有时无法正常工作。
我的代码中有错误吗?
Best Answer-推荐答案 strong>
来自苹果 docs可以看出,更新的发送频率并不高于每 5 分钟 和 500 米 的位置变化:
只要设备从之前的通知移动 500 米或更远,应用程序就会收到通知。它不应期望通知的频率超过每五分钟一次。如果设备能够从网络中检索数据,则位置管理器更有可能及时发送通知。
即使应用处于非事件状态,您也会收到更新。对您的另一个提示可能是 oyu 可以在模拟器中测试位置,而不是使用真实设备,这样您就不必到外面进行测试并且仍然可以检查您的日志。在模拟器菜单中,选择Debug --> Location。
关于ios - 应用程序处于非事件状态时获取位置更新在 2 次更新后停止,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/24277690/
|