2012-08-23 56 views
4

我们目前正在开发一个iOS应用程序需要在后台检查位置。起初,我们试图使用显着的位置变化,但它们不够准确/不经常触发。我们考虑过使用区域监控,但是从我在线阅读的内容来看,这并不总是准确的,而且您还有一个监控区域数量有限的问题。 (我们最终可能会尝试区域监控)。但是,目前我们正试图使用​​标准位置更新来跟踪用户在后台的位置,并计划每隔5分钟检查一次。后台任务似乎并没有被取消/结束

的应用程序注册为位置更新在后台(使用“应用程序注册了位置更新”为“所需的背景模式”),和我们启动一个后台任务,一次检查位置,停止位置更新,然后使用NSThread sleepForTimeInterval:到(目前,我们正在开发中)暂停任务10秒。然后,它再次检查位置,停止位置更新,暂停10秒,等

这似乎按预期方式工作......当应用程序进入后台,我们收到日志/通知我们的位置更新每10秒钟一次,当应用程序重新打开时,日志/通知将停止。然而,问题是,当应用程序第二次进入后台时,似乎原始后台任务从未取消,并且创建了新的后台任务,因此现在有两个任务正在运行,每个检查位置都在10秒间隔。如果应用程序被多次打开/发送到后台,则会为每个应用程序启动后台任务。

我想过设置一个标志来说“应用程序已经被发送到背景至少一次了?”,并且只在第一次发送到后台时才运行该任务,但这似乎会导致其他问题,(作为一个相对较新的iOS开发人员),我很好奇为什么后台任务在应用程序进入前台时未被取消。

的AppDelegate.h文件包含...

@interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate> { 
    UIWindow *window; 
    UINavigationController *navigationController; 

    UIBackgroundTaskIdentifier bgTask; 
    BOOL inBackground; 
} 

的AppDelegate.m文件包含...

- (void)applicationDidEnterBackground:(UIApplication *)application { 
    inBackground = YES; 

    bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 
     [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }]; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     while (inBackground == YES) { 
      NSLog(@"%@", @"Check location..."); 
      [locationManager startUpdatingLocation]; 

      [NSThread sleepForTimeInterval:10]; 
     } 

     [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }); 
} 

- (void)applicationWillEnterForeground:(UIApplication *)application 
{ 
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 
    inBackground = NO; 

    [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid; 
} 

更新是否按预期工作的位置,我只是不能工作为什么当应用程序进入前台时后台任务没有被取消/结束。我想知道这与NSThread sleepForTimeInterval:有什么关系,但我不确定它是否是,或者如何解决(如果的确如此)。预先感谢您的帮助!

+0

您应该将UIBackgroundMode添加到您的info.plist文件中,并将您的应用程序标识为背景位置应用程序。 – J2theC

+0

谢谢,J2theC,但是我们已经为'所需的背景模式'设置了'位置更新的应用寄存器'。 (我会编辑问题以清楚地说明这一点)。位置更新应该是他们应该做的,只是这个永无止境的后台任务就是问题所在。 – dvyio

+1

我很确定你的实例变量'bgTask'在应用程序回到前台时被重新分配,所以这个值不包含你想要杀死的标识符。考虑将这个标识符保存在'NSUserDefaults'中,或者更长一些。 – Hyperbole

回答

1

我相当肯定你的实例变量bgTask正在被重新分配,当应用程序回到前台时,所以这个值不包含你想要杀死的标识符。考虑将此标识符保存在NSUserDefaults或稍微更长的一些,稍后再检索。

+0

谢谢!我在' - (void)applicationWillEnterForeground:(UIApplication *)application'中注释了'bgTask = UIBackgroundTaskInvalid',并且还在' - (void)applicationDidEnterBackground:(UIApplication *)application'中添加了一个if/else, bgTask'存在。现在只创建一个新的'bgTask',如果它不存在,现在它似乎工作!你认为这是一个合理的解决方案吗?看起来'bgTask = UIBackgroundTaskInvalid'导致了一个问题,但是我认为这意味着在那里。 – dvyio

+0

将值设置为'UIBackgroundTaskInvalid'是您在完成标识符后应该执行的操作。我不明白你在评论中描述的是什么,你能否用你到达的代码更新你的答案? – Hyperbole

2

你不睡觉,然后要求他们管理位置更新。您可以通过在UIBackgroundMode设置“位置”(如你这样做),然后实施CLLocationManagerDelegate管理位置更新。这与beginBackgroundTaskWithExpirationHandler:无关。这是为了请求额外的时间(最多约10分钟)来完成给定的操作。你不应该只是为了获取位置更新而调用它。

一旦你在UIBackgroundMode注册为一个位置的应用程序,你会自动得到更新,每当你的位置管理器中指定的精度范围内的位置变化。系统将为您完成所有工作。


你描述实际上可能损害电池的寿命,因为它挫败操作系统的管理多个位置传感器(其中的GPS是其中一个)的能力。通过设置正确的准确度(如果重大更改过于粗糙),告诉操作系统您需要什么,并让它完成工作。从GPS获取真正准确的位置非常昂贵。在假定每5分钟更便宜的情况下,您应该进行电池测试,然后再开始测试。你可以做的最好的事情是保持力量,以降低所需的精度。你可以把它降到一个粗糙的水平,然后当你来到前台时,把它移到一个准确的水平。但保持跟踪精确地,其中用户每5分钟将是昂贵的。这很难解决。

顺便说一句,你真正想要做的就是每5分钟运行一次“东西”。 iOS中没有这种机制。您可以要求提供位置服务或不要(并以各种方式配置它)。你不能要求“我想每五分钟醒来......并且做任何事情。”大约10分钟后,如果你不打电话给endBackgroundTask:,你将会被杀。

要回答为什么这些任务未被取消的问题,请参阅How to use beginBackgroundTaskWithExpirationHandler for already running task in iOS。正如我所说,这个“后台任务”不是你想要解决这个问题的工具。这完全不相关。

+0

谢谢,罗布。这是我们最初尝试的,但我们不需要不断的位置更新。我们只需每隔x分钟就需要一个位置,同时仍然试图保存用户的电池。如果重要的位置变化或区域监测更准确,那么它们将是完美的,但我们需要比目前提供的更高的准确性。通过每隔x分钟检查一次位置(使用'CLLocationManagerDelegate'),然后立即关闭位置更新(直到下一次检查),我们可以在没有持续更新的情况下获得准确的位置。我想,它目前正在按照希望工作。 – dvyio

+0

正如你所建议的那样,我一定会尝试降低准确性的标准位置更新。理想情况下,我们每30分钟只需要一个用户的位置,但很明显,目前还不可能。如果有一个像重要的位置变化一样工作的API,那将是非常好的,但是在每移动100米(或左右)之后始终提供更新。请注意,如果您正在运行后台任务,则在该后台任务中请求位置更新时,该后台任务的剩余时间将被重置。 – dvyio

+0

如果你已经设置“位置”,那么你不应该在任何情况下被杀死。你不应该需要backgroundTask。如果你只需要100m的精度,那么设置kCLLocationAccuracyHundredMeters。如果你想要高准确度,但不想经常打电话,这不会有助于延长电池寿命。打电话给你很便宜。跟踪准确的位置是很昂贵的。如果您需要“有时”准确性,您可以设置kCLLocationAccuracyHundredMeters并进入后台。当你醒来时,检查准确性。然后设置kCLLocationAccuracyBest。如果您以更高的准确度再次醒来,请使用它。 –