2

我的算法希望以这种方式工作:
如果我的服务器状态发生变化,我会向我的应用程序发送远程通知(其状态可能会暂停或未运行)。但是,在通知用户之前,我希望运行一些代码。在 iOS 7 之前这是不可能的,但在 iOS 7 及更高版本中是可能的。(访问这里了解更多详情)

我希望运行的代码包括跟踪用户的位置一次然后停止。但是,跟踪位置的后台模式需要在前台开始更新位置,并且随着应用程序移动到后台CLLocationManager Delegate自动获取位置更新。但是,我只想在某个时间点跟踪一次,然后停止它。

PS:我的服务器状态可以随时改变。所以,我不能使用任何计时器或间隔

4

1 回答 1

0

我找到了问题/解决方案。当需要启动定位服务和停止后台任务时,应该延迟停止后台任务(我设置为 1 秒)。否则定位服务将无法启动。

另一个重要通知,iOS 7 中的最大后台时间现在是 3 分钟而不是 10 分钟。

也许它会帮助某人。

编辑:

在写了一个小例子之后,我发现定位服务也应该保持开启几秒钟(在我的例子中是 3 秒)。在“.plist”文件中将 UIBackgroundModes 设置为“位置”。例子:

ScheduledLocationManager.h

#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>

@interface ScheduledLocationManager : NSObject <CLLocationManagerDelegate>

-(void)getUserLocationWithInterval:(int ) interval ;

@end

ScheduledLocationManager.m

#import "ScheduledLocationManager.h"

int const kMaxBGTime = 170; //3 min - 10 seconds (as bg task is killed faster)
int const kTimeToGetLocations = 3;

@implementation ScheduledLocationManager{
    UIBackgroundTaskIdentifier bgTask;
    CLLocationManager *locationManager;
    NSTimer *checkLocationTimer;
    int checkLocationInterval;
    NSTimer *waitForLocationUpdatesTimer;
}

- (id)init
{
    self = [super init];
    if (self) {
        locationManager = [[CLLocationManager alloc] init];
        locationManager.delegate = self;
        locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        locationManager.distanceFilter = kCLDistanceFilterNone;

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    }
    return self;
}

-(void)getUserLocationWithInterval:(int ) interval
{
    checkLocationInterval = (interval > kMaxBGTime)? kMaxBGTime : interval;
    [locationManager startUpdatingLocation];
}

- (void)timerEvent:(NSTimer*)theTimer
{
    [self stopCheckLocationTimer];
    [locationManager startUpdatingLocation];

    // in iOS 7 we need to stop background task with delay, otherwise location service won't start
    [self performSelector:@selector(stopBackgroundTask) withObject:nil afterDelay:1];
}

-(void)startCheckLocationTimer
{
    [self stopCheckLocationTimer];
    checkLocationTimer = [NSTimer scheduledTimerWithTimeInterval:checkLocationInterval target:self selector:@selector(timerEvent:) userInfo:NULL repeats:NO];
}

-(void)stopCheckLocationTimer
{
    if(checkLocationTimer){
        [checkLocationTimer invalidate];
        checkLocationTimer=nil;
    }
}

-(void)startBackgroundTask
{
    [self stopBackgroundTask];
    bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        //in case bg task is killed faster than expected, try to start Location Service
        [self timerEvent:checkLocationTimer];
    }];
}

-(void)stopBackgroundTask
{
    if(bgTask!=UIBackgroundTaskInvalid){
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }
}

-(void)stopWaitForLocationUpdatesTimer
{
    if(waitForLocationUpdatesTimer){
        [waitForLocationUpdatesTimer invalidate];
        waitForLocationUpdatesTimer =nil;
    }
}

-(void)startWaitForLocationUpdatesTimer
{
    [self stopWaitForLocationUpdatesTimer];
    waitForLocationUpdatesTimer = [NSTimer scheduledTimerWithTimeInterval:kTimeToGetLocations target:self selector:@selector(waitForLoactions:) userInfo:NULL repeats:NO];
}

- (void)waitForLoactions:(NSTimer*)theTimer
{
    [self stopWaitForLocationUpdatesTimer];

    if(([[UIApplication sharedApplication ]applicationState]==UIApplicationStateBackground ||
        [[UIApplication sharedApplication ]applicationState]==UIApplicationStateInactive) &&
       bgTask==UIBackgroundTaskInvalid){
        [self startBackgroundTask];
    }

    [self startCheckLocationTimer];
    [locationManager stopUpdatingLocation];
}

#pragma mark - CLLocationManagerDelegate methods

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{    
    if(checkLocationTimer){
        //sometimes it happens that location manager does not stop even after stopUpdationLocations
        return;
    }

    //TODO: save locations

    if(waitForLocationUpdatesTimer==nil){
         [self startWaitForLocationUpdatesTimer];
    }
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    //TODO: handle error
}

#pragma mark - UIAplicatin notifications

- (void)applicationDidEnterBackground:(NSNotification *) notification
{
    if([self isLocationServiceAvailable]==YES){
        [self startBackgroundTask];
    }
}

- (void)applicationDidBecomeActive:(NSNotification *) notification
{
    [self stopBackgroundTask];
    if([self isLocationServiceAvailable]==NO){
        //TODO: handle error
    }
}

#pragma mark - Helpers

-(BOOL)isLocationServiceAvailable
{
    if([CLLocationManager locationServicesEnabled]==NO ||
       [CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied ||
       [CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted){
        return NO;
    }else{
        return YES;
    }
}


@end

您必须创建 ScheduledLocationManager 的实例并调用 getUserLocationWithInterval 以秒为单位传递一个间隔。

于 2013-10-20T05:57:26.330 回答