【问题标题】:iOS 7 background location service stopsiOS 7 后台定位服务停止
【发布时间】:2014-03-04 18:59:38
【问题描述】:

我正在构建一个在后台运行的 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 before startUpdatingLocation: 11.014648

iPhone 5: 21:06:45 backgroundTimeRemaining after startUpdatingLocation: 999.000000

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

iPhone 5: 21:06:48 backgroundTimeRemaining after 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

【问题讨论】:

    标签: ios service ios7 background location


    【解决方案1】:

    您通常不能确定您的应用会保持活动状态。用户可能想要主动终止它,或者系统可能需要您的应用程序正在使用的资源,因此终止它。由于资源限制,我猜您遇到了这个问题。

    您可以通过使用 iOS7 中引入的Background fetch 来绕过它。操作系统将定期允许您的应用重新启动。

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

    但是,您不能阻止用户强制关闭您的应用。

    【讨论】:

    • 感谢您的回答!但是,我认为这不是资源问题,因为我正在使用两部仅安装并运行应用程序的测试电话 - 没有其他应用程序。我会尝试后台获取,但是,据我所知,我不知道我什么时候会被唤醒,这取决于我自己的应用程序的使用情况?
    • 没错。根据 Apple 文档,如果您在启动时消耗少量时间,您的应用程序很可能会启动。您还可以在 Xcode 管理器中查找崩溃日志。 (连接 iPhone,在 Xcode 中选择 window->organizer。在左侧窗格中选择您的设备,然后单击 Device Logs)
    • 我现在已经实现了 Background Fetch,而且效果更好!功耗显着改善 - 我几乎无法检测到应用程序正在后台运行!然而,正如我们所讨论的,缺点是我无法控制它何时唤醒并联系我的服务器 - 但这是一个可以接受的权衡!感谢您的回答!
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      1. 将“UIBackgroundModes”添加到应用程序中 info.plist 文件中的字符串“locations”键

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

      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];

      【讨论】:

        【解决方案4】:

        应用程序将进入暂停模式,这就是您在此之后无法获得位置的原因。请检查以下网址。它将帮助您在 18 分钟内获得位置。甚至应用程序处于暂停模式。

        nice post related to background service

        【讨论】:

          猜你喜欢
          • 2019-03-03
          • 2014-01-05
          • 2013-12-30
          • 2020-08-09
          • 2013-09-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多