2014-02-07 40 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