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

我正在尝试的是startMonitoringSignificantLocationChangesregionMonitoring的组合。

  • 我在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
    



    编辑1:

    经过数次测试后,我对使用多种设备(iPhone 5s,iPad mini,iPhone 4)的汽车进行了很多实时测试:
  • 在一种情况下,当应用未运行且小箭头变为灰色时,iPad mini和iPhone 4会在几分钟后停止更新位置。
  • 当WiFi关闭时,准确性很差,并且位置很少更新。

  • 编辑2:

    好吧,经过大量的开车,四处走动和测试之后,到目前为止,它的工作原理仍然很不错。
    我设法使其工作正常,结合了significantLocationChanges和区域监视功能,始终在我的当前位置周围注册地理围栏,并始终在出现新的UIApplicationLaunchOptionsLocationKey时开始对位置进行重大更改。
    请注意,关闭wifi会使准确性非常低,甚至有时无法正常工作。

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

    最佳答案

    从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.
    即使该应用程序处于非 Activity 状态,您也将收到更新。给您的另一个提示是,oyu可以使用真正的设备来测试模拟器中的位置,这样您不必出门进行测试,仍然可以查看日志。在模拟器菜单中,选择“调试->位置”。

    10-01 22:46
    查看更多