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:,但我不确定是否是,或者如何修复它(如果确实如此)。提前感谢您的帮助!

4

2 回答 2

2

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

一旦您在 中注册为位置应用程序UIBackgroundMode,只要位置在您为位置管理器指定的精度范围内发生变化,您就会自动获取更新。系统将为您完成所有工作。


您所描述的内容实际上可能会损害电池寿命,因为它会破坏操作系统管理多个位置传感器(其中 GPS 只是其中之一)的能力。通过设置正确的准确性(如果重大更改太粗略)告诉操作系统您需要什么,然后让它完成它的工作。从 GPS 获得真正准确的位置非常昂贵。您应该先进行电池测试,然后再假设每 5 分钟进行一次比继续开机更便宜。保存功率的最佳方法是降低所需的精度。你可以把它调低到一个粗略的水平,然后当你来到前景时将它移动到一个准确的水平。但是每 5 分钟准确跟踪一次用户的位置会很昂贵。很难解决这个问题。

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

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

于 2012-08-23T18:29:36.580 回答
1

我相当确定bgTask当应用程序返回前台时您的实例变量正在重新分配,因此该值不包含您要杀死的标识符。考虑将此标识符保存在NSUserDefaults或更永久的位置,然后再检索它。

于 2012-08-24T14:27:02.353 回答