由 iBeacon 触发的 iOS 8 后台位置更新

标签 ios objective-c iphone gps ibeacon

我正在尝试制作一个可以由 iBeacon 触发唤醒(从被杀死/暂停/终止)以记录每秒 GPS 信息的应用程序。当手机超出信标范围时,GPS 记录应该会停止。我已经成功地让我的应用程序在进入和超出 iBeacon 范围时识别 didEnterRegion 和 didExitRegion 方法。在 didEnterRegion 方法中,我基本上想说类似 [locationManager startUpdatingLocation] 的内容,以便我可以开始跟踪用户的位置。但是,当我尝试添加这行代码时,位置更新会在大约 10 秒后停止。

后来我发现了一个article关于此 Github 附带的后台位置更新 project .我将 BackgroundTaskManager、LocationShareModel 和 LocationTracker 文件添加到我的项目中。基本上,这个解决方案背后的想法是不断重启位置管理器,这样它就没有机会让后台任务过期并停止发送更新。但是,即使使用此解决方案,我也只能获得 3 分钟多一点的位置更新。

我启用了“位置更新”和“使用蓝牙 LE 配件”后台模式。 “后台获取”(后台应用程序刷新)未启用,根据 Apple 的引述:“在 iOS 8 及更高版本中,禁用当前应用程序或所有应用程序的后台应用程序刷新设置不会阻止位置的传递背景中的事件。”我的应用请求位置更新的“始终”授权。

尽管查阅了看似无穷无尽的 StackOverflow 文章和教程,但我还是想不出如何解决这个问题。我正在运行 iOS 8.3.0 的 iPhone 5S 上对其进行测试。任何见解将不胜感激。请参阅下面的代码摘录。

在 AppDelegate.m 中:

- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
    if ([region isKindOfClass:[CLBeaconRegion class]]) {
        UILocalNotification *notification = [[UILocalNotification alloc] init];
        notification.alertBody = @"Start recording trip";
        notification.soundName = @"Default";
        [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

        self.recording = YES;
        [self startAutoTrip];
    }
}

- (void) startAutoTrip {
    self.locationTracker = [[LocationTracker alloc]init];
    [self.locationTracker startLocationTracking];
}

- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    if ([region isKindOfClass:[CLBeaconRegion class]]) {
        UILocalNotification *notification = [[UILocalNotification alloc] init];
        notification.alertBody = @"Stop recording trip";
        notification.soundName = @"Default";
        [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

        [self stopAutoTrip];
        self.recording = NO;
    }
}

- (void)stopAutoTrip {

    // stop recording the locations
    CLSLog(@"Trying to stop location updates");
    [self.locationTracker stopLocationTracking:self.managedObjectContext];
    CLSLog(@"Stop location updates");
}

在 LocationTracker.m 中(从上面引用的教程中,将 60 秒和 10 秒的时间间隔更改为 5 秒和 2 秒)。基本上这些是 startLocationTracking、didUpdateLocations 和 stopLocationTracking 方法。

- (void)startLocationTracking {
NSLog(@"startLocationTracking");

    if ([CLLocationManager locationServicesEnabled] == NO) {
        NSLog(@"locationServicesEnabled false");
        UIAlertView *servicesDisabledAlert = [[UIAlertView alloc] initWithTitle:@"Location Services Disabled" message:@"You currently have all location services for this device disabled" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [servicesDisabledAlert show];
    } else {
        CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];

        if(authorizationStatus == kCLAuthorizationStatusDenied || authorizationStatus == kCLAuthorizationStatusRestricted){
            NSLog(@"authorizationStatus failed");
        } else {
            NSLog(@"authorizationStatus authorized");
            CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
            locationManager.delegate = self;
            locationManager.desiredAccuracy = kCLLocationAccuracyBest;
            locationManager.distanceFilter = 10; //meters
            locationManager.activityType = CLActivityTypeAutomotiveNavigation;
            locationManager.pausesLocationUpdatesAutomatically = NO;

            if(IS_OS_8_OR_LATER) {
                [locationManager requestAlwaysAuthorization];
            }
            [locationManager startUpdatingLocation];
        }
    }
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{

    NSLog(@"locationManager didUpdateLocations");

    for(int i=0;i<locations.count;i++){
        CLLocation * newLocation = [locations objectAtIndex:i];

        NSDate *eventDate = newLocation.timestamp;

        NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];

        if (fabs(howRecent) < 10.0 && newLocation.horizontalAccuracy < 20 && locations.count > 0) {


            CLLocationCoordinate2D theLocation = newLocation.coordinate;
            CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;

            self.myLastLocation = theLocation;
            self.myLastLocationAccuracy= theAccuracy;

            CLLocationCoordinate2D coords[2];
            coords[0] = ((CLLocation *)locations.lastObject).coordinate;
            coords[1] = newLocation.coordinate;

            [self.shareModel.myLocationArray addObject:newLocation];

        }
    }

    //If the timer still valid, return it (Will not run the code below)
    if (self.shareModel.timer) {
        return;
    }

    self.shareModel.bgTask = [BackgroundTaskManager sharedBackgroundTaskManager];
    [self.shareModel.bgTask beginNewBackgroundTask];

    //Restart the locationMaanger after 1 minute (5 sec)
    self.shareModel.timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self
                                                       selector:@selector(restartLocationUpdates)
                                                       userInfo:nil
                                                        repeats:NO];

    //Will only stop the locationManager after 10 seconds, so that we can get some accurate locations
    //The location manager will only operate for 10 seconds to save battery
    // 2 sec
    if (self.shareModel.delay10Seconds) {
        [self.shareModel.delay10Seconds invalidate];
        self.shareModel.delay10Seconds = nil;
    }

    self.shareModel.delay10Seconds = [NSTimer scheduledTimerWithTimeInterval:2 target:self
                                                selector:@selector(stopLocationDelayBy10Seconds)
                                                userInfo:nil
                                                 repeats:NO];

}

- (void) restartLocationUpdates
{
    NSLog(@"restartLocationUpdates");

    if (self.shareModel.timer) {
        [self.shareModel.timer invalidate];
        self.shareModel.timer = nil;
    }

    CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
    locationManager.delegate = self;
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    locationManager.distanceFilter = 10; //meters
    locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    locationManager.pausesLocationUpdatesAutomatically = NO;

    if(IS_OS_8_OR_LATER) {
        [locationManager requestAlwaysAuthorization];
    }
    [locationManager startUpdatingLocation];
}

- (void)stopLocationTracking:(NSManagedObjectContext *)managedObjectContext       {
    NSLog(@"stopLocationTracking");
    CLSLog(@"stopLocationTracking");

    CLSLog(@"set managedObjectContext %@", managedObjectContext);
    self.managedObjectContext = managedObjectContext;

    if (self.shareModel.timer) {
        [self.shareModel.timer invalidate];
        self.shareModel.timer = nil;
    }

    CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
    [locationManager stopUpdatingLocation];

    [self saveRun];
    [self sendRun];
}

最佳答案

谢谢大家的回复。可以使用 iBeacons 将您的应用程序从被杀死/暂停/终止中唤醒,这与Øyvind Hauge相反。说。不幸的是,将后台位置模式添加到您的 plist 中并不能像其他人建议的那样启用无限的位置更新;使用该方法我只能执行 3 分钟。

我实际上在 StackOverflow article 中找到了我的问题的解决方案。解决方案是向您的应用委托(delegate)添加几行代码——您需要启动另一个位置管理器来监视重要的位置更新。以下是我在将 anotherLocationManager 声明为属性后添加到 AppDelegate.m 文件中的 didFinishLaunchingWithOptions 方法的代码行...

self.anotherLocationManager = [[CLLocationManager alloc] init];
self.anotherLocationManager.delegate = self;
[self.anotherLocationManager startMonitoringSignificantLocationChanges];

我从来没有使用这个位置管理器做任何其他事情,我只是让它在后台永久运行,出于某种原因,这使得通过定期调用 [locationManager startUpdatingLocation] 可以无限期地更新位置。我不再遇到位置更新在 3 分钟后神秘停止的情况。这个解决方案看起来很奇怪,但实现起来非常简单,希望这能帮助其他正在处理同样问题的人。

关于由 iBeacon 触发的 iOS 8 后台位置更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31865234/

相关文章:

ios - AVAudioSession setCategory 总是返回错误

ios - 设置音量属性时 AVAudioFoundation 出现问题

objective-c - 如何防止框架中标记为 "Project"的 header 被公开复制?

iphone - 使用 NSFetchedResult Controller 处理节标题

objective-c - AVFoundation 文档代码

objective-c - UIView Image 可以在其框架外绘制图像吗?

ios - 如何在通知中心频繁更新今日小部件?

iPhone "Share"按钮

ios - 使用未在 iOS 8.4.1 中安装的 BetaBuilder 创建的临时 OTA 构建

ios - 处理 Apple 推送通知服务