应用程序处于非活动状态时获取位置更新会在2次更

时间:2014-06-18 05:31:19

标签: ios objective-c cllocationmanager

我在我的应用处于非活动状态时尝试获取位置更新(用户关闭了应用)。 在2次位置更新后,位置更新将停止启动我的应用。 指标是我的应用程序中位置服务设置中的灰色箭头。

我尝试的是startMonitoringSignificantLocationChanges& regionMonitoring

  • 我在iPhone 4 iOS 7.1.1上测试过,2次更新后位置更新停止。
  • 我在iPad mini WiFi + Cellular iOS 7.1.1中进行了测试,位置更新在1次更新后停止,区域监控仅发送1个位置。

我哪里错了?

我的代码:

AppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)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)applicationDidEnterBackground:(UIApplication *)application
{    
    [defaults synchronize];

    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;

    if ([RegionMonitoringService sharedInstance].launchOptions[UIApplicationLaunchOptionsLocationKey]) {
        return;
    }
}    

- (void)application:(UIApplication *)application handleNewLocationEvet:(NSDictionary *)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)startMonitoringForRegion:(CLRegion *)region;
- (void)startMonitoringRegionWithCoordinate:(CLLocationCoordinate2D)coordinate andRadius:(CLLocationDirection)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 *)identifierForCoordinate:(CLLocationCoordinate2D)coordinate;
- (CLLocationDistance)getFixRadius:(CLLocationDistance)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

enter image description here

enter image description here

编辑1:

经过几次测试后,我用多种设备(iPhone 5s,iPad mini,iPhone 4)进行了大量实时测试:

  1. 在一个案例中,iPad mini&amp;当应用程序未运行且小箭头变为灰色时,iPhone 4会在几分钟后停止更新位置。
  2. 当WiFi关闭时,准确性很差,且位置很少更新。
  3. 编辑2:

    好的,经过大量的驾驶和走动并进行测试后,到目前为止它的功能就像一个魅力。 我设法让它工作,结合significantLocationChanges&amp;区域监控,始终在我当前位置周围注册地理围栏,并在新UIApplicationLaunchOptionsLocationKey到来时始终开始重要的位置更改。 请注意,关闭wifi会使准确度非常低,有时甚至无法正常工作。

    我的代码中有任何错误吗?

1 个答案:

答案 0 :(得分:1)

从Apple docs可以看出,每次 5分钟以及 500米位置更改时,更新的发送频率不会更高:

Apps can expect a notification as soon as the device moves 500 meters or more from its previous notification. It should not expect notifications more frequently than once every five minutes. If the device is able to retrieve data from the network, the location manager is much more likely to deliver notifications in a timely manner.

即使应用处于非活动状态,您也会收到更新。另一个暗示可能是oyu可以使用真实设备测试模拟器中的位置,这样您就不必去外面进行测试,仍然可以检查您的日志。在模拟器菜单中,选择 Debug - &gt;位置