后台任务似乎没有被取消/结束
Posted
技术标签:
【中文标题】后台任务似乎没有被取消/结束【英文标题】:Background task doesn't seem to get cancelled/ended 【发布时间】:2012-08-19 02:34:35 【问题描述】:我们目前正在开发一个需要在后台检查位置的 ios 应用。起初,我们尝试使用重大的位置变化,但它们不够准确/触发频率不够高。我们考虑过使用区域监控,但从我在网上阅读的内容来看,这也不总是准确的,而且您还存在要监控的区域数量有限的问题。 (我们最终可能会尝试区域监控。)然而,目前,我们正在尝试使用标准位置更新来在后台跟踪用户位置,并计划每隔 5 分钟左右检查一次。 .
应用程序在后台注册位置更新(使用“应用程序注册位置更新”作为“必需的后台模式”),我们启动一个后台任务,它检查一次位置,停止位置更新,然后使用@987654321 @to(目前,我们正在开发中)暂停任务 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:
有关,但我不确定是否是,或者如何修复它(如果确实如此)。提前感谢您的帮助!
【问题讨论】:
您应该将 UIBackgroundMode 设置为 info.plist 文件并将您的应用程序标识为后台位置应用程序。 谢谢,J2theC,但我们已经为“所需的后台模式”提供了“用于位置更新的应用程序注册”。 (我将编辑问题以明确这一点。)位置更新按应有的方式发生,只是这个永无止境的后台任务才是问题所在。 我很确定当应用程序回到前台时您的实例变量bgTask
正在重新分配,因此该值不包含您要杀死的标识符。考虑将此标识符保存在 NSUserDefaults
或更永久的位置。
【参考方案1】:
您不能通过睡觉然后请求它们来管理位置更新。您可以通过在UIBackgroundMode
中设置“位置”来管理位置更新(就像您所做的那样),然后实现CLLocationManagerDelegate
。这与beginBackgroundTaskWithExpirationHandler:
无关。那是为了请求额外的时间(最多大约 10 分钟)来完成给定的操作。您根本不应该仅仅为了获取位置更新而调用它。
在UIBackgroundMode
中注册为位置应用程序后,只要位置在您为位置管理器指定的精度范围内发生变化,您就会自动获得更新。系统将为您完成所有工作。
您所描述的内容实际上可能会损害电池寿命,因为它会影响操作系统管理多个位置传感器(GPS 只是其中之一)的能力。通过设置正确的准确性(如果重大更改太粗略)告诉操作系统您需要什么,然后让它完成它的工作。从 GPS 获得真正准确的位置非常昂贵。您应该先进行电池测试,然后再假设每 5 分钟进行一次比继续开机更便宜。保存功率的最佳方法是降低所需的精度。你可以把它调低到一个粗略的水平,然后当你来到前景时将它移动到一个准确的水平。但是,每 5 分钟精确跟踪一次用户的位置会很昂贵。很难解决这个问题。
顺便说一句,您在这里真正想做的是每 5 分钟运行一次“某事”。 iOS 中没有这种机制。您可以要求或不要求位置服务(并以各种方式进行配置)。你不能要求“我想每五分钟醒来一次,然后……做任何事”。大约 10 分钟后,如果你不打电话给 endBackgroundTask:
,你就会被杀死。
关于为什么任务没有被取消的问题,请参阅How to use beginBackgroundTaskWithExpirationHandler for already running task in iOS。正如我所说,这个“后台任务”不是你想要解决这个问题的工具。完全不相关。
【讨论】:
谢谢,罗伯。这是我们最初尝试过的,但我们不需要不断更新位置。我们只需要每 x 分钟一次的位置,同时仍试图节省用户的电池电量。如果重大的位置变化或区域监控更准确,它们将是完美的,但我们需要比它们目前提供的更准确。通过每 x 分钟检查一次位置(使用CLLocationManagerDelegate
),然后立即关闭位置更新(直到下一次检查),我们可以获得准确的位置,而无需不断更新。我认为它目前正在按预期工作。
我肯定会按照您的建议尝试降低准确性的标准位置更新。理想情况下,我们每 30 分钟只需要一个用户的位置,但显然,这目前是不可能的。如果有一个 API 可以像显着的位置变化一样工作,但在每 100 米(左右)移动后始终提供更新,那就太好了。附带说明一下,如果您正在运行后台任务,然后在该后台任务中请求位置更新,则该后台任务的允许剩余时间被重置。
如果你设置了“位置”,那么无论如何你都不应该被杀死。你不应该需要背景任务。如果只需要 100m 精度,则设置 kCLLocationAccuracyHundredMeters。如果您想要高精度,但又不想经常被调用,那将无助于延长电池寿命。打电话给你很便宜。跟踪准确的位置是昂贵的。如果您“有时”需要准确性,您可以设置 kCLLocationAccuracyHundredMeters 并进入后台。当你醒来时,检查准确性。然后设置 kCLLocationAccuracyBest。如果您再次以更高的准确度醒来,请使用它。【参考方案2】:
我相当确定当应用程序返回前台时,您的实例变量 bgTask
正在重新分配,因此该值不包含您要杀死的标识符。考虑将此标识符保存在 NSUserDefaults
或其他更永久的位置并稍后检索它。
【讨论】:
谢谢!我在- (void)applicationWillEnterForeground:(UIApplication *)application
中注释掉了bgTask = UIBackgroundTaskInvalid
,还在- (void)applicationDidEnterBackground:(UIApplication *)application
中添加了一个if/else,以查看bgTask
是否存在。如果它不存在,它现在只会创建一个新的bgTask
,而且它现在似乎可以工作了!您认为这是一个合理的解决方案吗?似乎bgTask = UIBackgroundTaskInvalid
引起了问题,但我认为它应该存在。
将值设置为UIBackgroundTaskInvalid
是您完成标识符后应该做的事情。我不明白你在评论中描述了什么,你能用你得到的代码更新你的答案吗?以上是关于后台任务似乎没有被取消/结束的主要内容,如果未能解决你的问题,请参考以下文章