iOS 7后台位置服务停止

时间:2014-02-07 11:24:39

标签: ios service ios7 background location

我正在构建一个在后台运行的iOS应用程序,并且每隔3分钟将用户的位置发布到服务器(因为这是iOS 7上的最大后台执行时间)。 但是,有一个问题,后台服务随机终止。所以有时它可以在后台运行2个小时,有时可以运行7个小时,然后是3个小时,随机运行。

以下代码会产生错误。 我已经能够检测何时它将终止,那时 [UIApplication sharedApplication] .backgroundTimeRemaining 低于10秒。 任何人都可以指出某个方向,或解释为什么它会终止?我的猜测是[self.locationManager startUpdatingLocation]不是100%安全吗? (以及使[UIApplication sharedApplication] .backgroundTimeRemaining无限制的方法?)

以下是我每3分钟在服务器上收到的“正常”日志;

iPhone 5:21:06:45 backgroundTimeRemaining在startUpdatingLocation之前:11.014648

iPhone 5:21:06:45在startUpdatingLocation之后的backgroundTimeRemaining:999.000000

iPhone 5:21:06:48 backgroundTimeRemaining before stopUpdatingLocation:999.000000

iPhone 5:21:06:48 backgroundTimeRemaining stopUpdatingLocation:999.000000

代码:

#import "BetaLocationController.h"
#import "AppDelegate.h"
#import <Parse/Parse.h>
#import "CommunicationController.h"

@interface BetaLocationController () <CLLocationManagerDelegate>

@property UIBackgroundTaskIdentifier bgTask;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property BOOL locationStarted;
@property (strong, nonatomic) CLLocation *myLocation;
@end

@implementation BetaLocationController

- (id)init
{
    self = [super init];
    if (self) {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
        self.locationManager.pausesLocationUpdatesAutomatically = NO;
        self.locationManager.activityType = CLActivityTypeOther;
        self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
        self.locationManager.distanceFilter = kCLDistanceFilterNone;

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

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation* location = [locations lastObject];
    self.myLocation = location;
    //    NSLog(@"Location: %f, %f", location.coordinate.longitude, location.coordinate.latitude);

}

- (void)didEnterBackground:(NSNotification *) notification
{
    [self runBackgroundTask:10];
}


-(void)runBackgroundTask: (int) time{
    //check if application is in background mode
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {

        //create UIBackgroundTaskIdentifier and create tackground task, which starts after time
        __block UIBackgroundTaskIdentifier bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
            //            [self runBackgroundTask:5];
            [[UIApplication sharedApplication] endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;
        }];

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            NSTimer *t = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startTrackingBg) userInfo:nil repeats:NO];
            [[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
            [[NSRunLoop currentRunLoop] run];
        });
    }
}

-(void)startTrackingBg{
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    NSDate *now = [NSDate date];
    localNotification.fireDate = now;

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"HHmm"];
    int timeofDay = [[formatter stringFromDate:[NSDate date]] intValue];
    localNotification.applicationIconBadgeNumber = timeofDay;
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

    //set default time
    int time = 170;
    //if locationManager is ON
    if (self.locationStarted  == TRUE ) {
        //Here I can detect the error
        if([UIApplication sharedApplication].backgroundTimeRemaining < 10){
            [CommunicationController logToParse:[NSString stringWithFormat:@"%@ : Detected error, trying to restart locationservice", [UIDevice currentDevice].name]];
        }

        //log
        [CommunicationController logToParse:[NSString stringWithFormat:@"%@: %@ bgTime before stopUpdatingLocation: %f", [UIDevice currentDevice].name, [NSDate date], [self getBackgroundTime]]];

        [self.locationManager stopUpdatingLocation];
        self.locationStarted = FALSE;

        //log
        [CommunicationController logToParse:[NSString stringWithFormat:@"%@: %@ bgTime after stopUpdatingLocation: %f", [UIDevice currentDevice].name, [NSDate date], [self getBackgroundTime]]];

    }else{
        //start updating location

        //log
        [CommunicationController logToParse:[NSString stringWithFormat:@"%@: %@ bgTime before startUpdatingLocation: %f", [UIDevice currentDevice].name, [NSDate date], [self getBackgroundTime]]];

        [self.locationManager startUpdatingLocation];
        self.locationStarted = TRUE;

        //Time how long the application will update your location
        time = 3;

        //log
        [CommunicationController logToParse:[NSString stringWithFormat:@"%@: %@ bgTime after startUpdatingLocation: %f", [UIDevice currentDevice].name, [NSDate date], [self getBackgroundTime]]];

    }

    [self runBackgroundTask:time];
}

-(float)getBackgroundTime{
    float bgTime = [[UIApplication sharedApplication] backgroundTimeRemaining];

    //If bgtime is over 180, it returns unlimited. In iOS 7, only 3 minutes backgroundtime is available
    return bgTime > 180 ? 999 : bgTime;
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    [CommunicationController logToParse:[NSString stringWithFormat:@"%@: %@ LocationManagerDidFail %@: %f", [UIDevice currentDevice].name, [NSDate date], error, [[UIApplication sharedApplication] backgroundTimeRemaining]]];

    NSLog(@"Error in locationManager. It Failed: %@", error);
}


-(void)dealloc {
    [CommunicationController logToParse:[NSString stringWithFormat:@"%@: %@ got dealloc'ed", [UIDevice currentDevice].name, [NSDate date]]];

    NSLog(@"Dealloc in location manager is called");
}


@end

4 个答案:

答案 0 :(得分:0)

您通常无法确定您的应用是否会保持活力。用户可能希望主动终止它,或者系统可能需要您的应用程序正在使用的资源,因此终止它。我猜你因资源限制而遇到这个问题。

您可以使用iOS7中引入的Background fetch来解决这个问题。操作系统将定期允许您的应用再次启动。

尝试阅读这篇描述该功能的优秀文档:http://devstreaming.apple.com/videos/wwdc/2013/204xex2xvpdncz9kdb17lmfooh/204/204.pdf

但是,您可以阻止用户强制关闭您的应用。

答案 1 :(得分:0)

您无法在iOS7的后台启动位置服务。这就是它不一致的原因。您需要在应用程序位于前台时启动它并将其保持打开状态。

答案 2 :(得分:0)

按照以下步骤获取后台模式中的位置:

  1. 添加&#34; UIBackgroundModes&#34;到一个字符串&#34;位置&#34;键入应用程序中的info.plist文件 enter image description here

  2. 编辑/添加您的代码:

  3. &#13;
    &#13;
    if(self.location==nil){
         self.locationManager = [[CLLocationManager alloc] init];
    }
    [self.locationManager setDelegate:self];
    
    if( [self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
    {
         [self.locationManager requestAlwaysAuthorization];
    }
    
    self.locationManager.distanceFilter = 10.0f;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    self.locationManager.pausesLocationUpdatesAutomatically=NO;
    self.locationManager.activityType=CLActivityTypeAutomotiveNavigation;
    
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8) {
    [self.locationManager requestAlwaysAuthorization];
    }
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9) {
         self.locationManager.allowsBackgroundLocationUpdates = YES;
    }
    
    [self.locationManager startUpdatingLocation];
    &#13;
    &#13;
    &#13;

答案 3 :(得分:0)

应用程序将继续执行暂停模式,这就是为什么在此之后您无法获得该位置的原因。请查看以下网址。它可以帮助您获得超过18分钟的位置。即使应用程序处于暂停模式。

nice post related to background service