2014-02-07 84 views
0

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

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

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

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

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

iPhone 5:stopUpdatingLocation前21时06分48秒backgroundTimeRemaining: 999.000000

iPhone 5:stopUpdatingLocation后,21时06分48秒backgroundTimeRemaining: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 

回答

0

可以在一般不能确定你的应用程序将保持活跃。用户可能想要主动终止它,或者系统可能需要您的应用程序正在使用的资源,并因此终止它。我想你由于资源限制而遇到了这个问题。

您可以使用iOS7中引入的Background fetch来避开它。操作系统将定期让您的应用程序重新启动。

尝试阅读本描述功能出色的文件:http://devstreaming.apple.com/videos/wwdc/2013/204xex2xvpdncz9kdb17lmfooh/204/204.pdf

可以,但不能阻止强制用户您的应用程序关闭。

+0

感谢您的回答!但是,我不认为这是一个资源问题,因为我正在使用两台只安装了应用程序并正在运行的测试电话 - 没有其他应用程序。 但是,我会尝试后台获取,因为我的理解我无法知道什么时候会被唤醒,而这取决于我自己的应用程序的用法? – kbjeppesen

+0

这是正确的。根据Apple的文档,如果你开始使用少量的时间,你的应用程序可能会启动得更多。您也可以在Xcode组织者中查找crashlog。 (连接iPhone,在Xcode中选择窗口 - >管理器,在左侧窗格中选择您的设备,然后单击设备日志) – EsbenB

+0

我现在已经实现了后台抓取,它的工作原理非常好!功耗显着提高 - 我几乎无法检测到应用程序在后台运行!然而,正如我们已经讨论的那样,缺点是我无法控制什么时候会唤醒并联系我的服务器 - 但这是一个可以接受的折衷!感谢你的回答! – kbjeppesen

0

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

0

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

  1. 添加“UIBackgroundModes”的字符串中的信息“位置”键。在你的应用plist文件 enter image description here

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

0

应用程序将挂起模式的移动,这就是为什么你会不会得到后的位置那。请检查下面的URL。它会帮助你获得超过18分钟的位置。即使应用程序处于暂停模式。

nice post related to background service