如何在iOS应用程序中在线后台更新位置?

3
我正在尝试构建一个iPhone应用程序,需要使用Core Location跟踪用户的位置。我使用startMonitoringSignificantLocationChanges以便在后台更新位置,并且准确度不那么重要,只要在有重大变化时进行更新即可。
当前,我通过向Web服务发出HTTP请求来更新位置。当我运行应用程序时,这样做完全正常,我的位置会在存储位置数据的MySQL数据库中更新。但是,当应用程序进入后台时——我可以看到手机右上角仍在运行位置服务图标,但当我返回并查看数据库时,它根本没有更新我的位置。我通过驾车横穿城镇进行了测试,一次是运行应用程序,一次是应用程序在后台运行。
从苹果公司的文档中得知:

如果您让此服务保持运行,并且随后暂停或终止您的应用程序,则该服务将在新的位置数据到达时自动唤醒您的应用程序。在唤醒时间,您的应用程序被置于后台,并被给予少量时间来处理位置数据。因为您的应用程序在后台运行,所以应最小化工作并避免任何可能防止其在分配的时间到期之前返回的任务(如查询网络)。如果不这样做,您的应用程序可能会被终止。

“少量时间来处理位置数据”到底有多长?只是不建议查询网络,还是在那段时间内无法查询网络?是否有更好的方法来跟踪多个用户的位置,即使应用程序在后台运行?
1个回答

4

您应该查看后台任务。

当苹果公司说“在处理位置数据时只需要很少的时间”时,您不应该依赖于在locationManager:didUpdateToLocation:fromLocation:方法返回后获得任何处理时间。假设您在单独的线程中异步运行HTTP请求,您可能没有足够的时间在应用程序暂停之前完成它。

UIBackgroundTasks让您在后台请求操作系统提供额外的处理时间。发出HTTP请求可能就是这样一个任务。虽然无法保证能获得10分钟的时间限制,但也可申请。

在您的位置回调中,您应为请求定义一个新的后台任务。如果操作系统决定无法再提供更多处理时间,则到期处理程序块将在任何时候触发。

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    // Start your request here
    UIBackgroundTaskIdentifier backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        backgroundTask = UIBackgroundTaskInvalid;
        // Cancel your request here
    }];
}

当请求结束时,您应该告诉应用程序任务已完成:

- (void)requestFinished:(id)request {
    [[UIApplication sharedApplication] endBackgroundTask:backgroundTask];
    backgroundTask = UIBackgroundTaskInvalid;
}

在这个示例中,我没有考虑到在请求完成之前您可能会收到多个位置回调。如果发生这种情况,您需要取消该请求和当前的后台任务,然后再开始一个新的请求,或者为每个请求创建一个单独的后台任务。
另一种方法是在位置回调方法中在主线程上同步运行HTTP请求,但出于多种原因,这将是不好的做法,比如如果用户在请求正在运行时打开应用程序,则会锁定界面。

这个很好用。不过有一个问题 - 它是如何确定在后台执行哪个代码块的呢?它只是在启动后台任务的整个函数中执行吗? - unsunghero
2
当开始一个后台任务时,你基本上是在告诉系统你有一个要执行的任务,在这种情况下是一个HTTP请求。系统不知道你在开始和结束任务之间做了什么,这就是为什么它会给你UIBackgroundTaskIdentifier,以便在任务完成时报告回来。我的示例代码可能有点不清楚,因为我将UIBackgroundTaskIdentifier声明为局部变量。它应该是一个实例变量,当请求完成并传递到endBackgroundTask:时可以访问。 - Anton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接