3

我正在为iOS 7创建基于导航的应用程序,为此我正在获取用户位置数据,使用 CoreLocation 框架,

应用程序要求是在特定时间开始在后台获取用户位置,为此我已经使用didReceiveRemoteNotification fetchCompletionHandler:方法实现了 Silent Pushnotification,

我已经成功地实现了这个 Using Silent Pushnotification & it CallstartUpdatingLocation并且我能够在委托方法中获取位置数据:

使用此有效负载:

{"aps" : {"content-available" : 1},"SilentPush" : "4"}

我为后台模式启用了location& :remote notification

在此处输入图像描述

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
     __block UIBackgroundTaskIdentifier bgTask =0;
    UIApplication  *app = [UIApplication sharedApplication];
     bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [self.locationManager startUpdatingLocation];

 }];

didUpdateLocations

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
        lastLoc=[locations lastObject];
        [logFile addLocObject:[NSString stringWithFormat:@"Loc: %@",lastLoc]];
}

但问题是:

几秒钟后,位置类的委托方法停止,如果设备正在移动,它不会发送任何数据,当我将应用程序与前台交谈时,它会调用“didFinishLaunhing”方法,所以我猜即使位置正在更新,操作系统也会杀死应用程序,在Diagnostics & usage我收到以下崩溃报告的设备:

Application Specific Information:
MockUpApp2[390] has active assertions beyond permitted time: 
{(
    <BKProcessAssertion: 0x145ac790> identifier: Called by MockUpApp2, from -[AppDelegate application:didReceiveRemoteNotification:fetchCompletionHandler:] process: MockUpApp2[390] permittedBackgroundDuration: 40.000000 reason: finishTaskAfterBackgroundContentFetching owner pid:390 preventSuspend  preventIdleSleep  preventSuspendOnSleep 
)}

昨天我问了这个问题,现在我可以通过推送通知在后台启动位置管理器,

所以请任何人都可以解决这个问题。

谢谢你。

注意: 如果我在调试模式下运行应用程序,意味着我通过 XCode 运行应用程序并且不停止它或不断开连接,应用程序将运行。在这种情况下,应用程序不会被操作系统停止。

编辑 1

根据@Stephen Darlington 的回答,如果我删除了所有的 backgroundfacher 之类的,

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
        [self.locationManager startUpdatingLocation];

}

现在应用程序甚至不会调用didUpdateLocations一次,:(

我应该用这种方法写什么?

Edit 2

根据Apple Doc说:

If your app is suspended or not running, the system wakes up or launches your app and puts it into the background running state before calling the method.

那么,只要我启用了位置背景模式,应用程序是否应该在后台运行?

4

3 回答 3

3

我不认为 iOS 上的后台处理以您期望的方式工作。它不像在 Mac 或 Windows 上可以无限期地在后台运行(即使在 iOS 7 中进行了更改)。

所以,直接回答你的问题:你启动一个可以继续在后台运行的任务。有两个“但是”。

  1. 代码块是过期处理程序。这不是您要在后台运行的代码。此代码将在后台任务超时之前执行(因此在您的情况下,它会在您的应用程序被终止之前不久执行)。苹果并没有真正记录那是多长时间,但看起来好像你得到了大约 40 秒。您要在后台运行的代码 beginBackgroundTaskWithExpirationHandler. 完成后你说endBackgroundTask:
  2. 其次,位置更新在后台工作,没有所有beginBackgroundTaskWithExpirationHandler:东西。即使应用程序在后台,也会调用委托方法

总结:删除beginBackgroundTaskWithExpirationHandler:语句。您只需添加startUpdatingLocation:.

于 2014-02-07T11:58:26.560 回答
2

前段时间我用 CLLocation 做了很多工作,我有一些意见和建议,虽然可能不是完整的解决方案。

我看到您正在对无声通知做出反应beginBackgroundTaskWithExpirationHandler。我不认为这是正确的。但是,我确实相信您应该在 CLLocationDelegate 方法上使用该 backgroundTask 模式。

更重要的是,Apple 很有可能会拒绝您将位置标志用于后台进程。借鉴我的经验。我花了很多时间来合理地使用定位服务来提高我自己流程的准确性。我什至启用了许多防止过度使用电池的保护措施,他们没有讨论就拒绝了。我做到了,所以它只在设备插入充电器时才使用后台服务。我上诉并为负责任的使用提出了很好的理由,但无济于事。

您应该查看条款并确保您的用例完全按照说明包含在内,否则它不会浮动。

我还是说不用后台服务也能得到你想要的。使用显着的位置变化和区域监控您的应用程序只需要保存最新的已知位置并响应通知即可。

我会想到一个这样的方案:

  • 发生重大位置变化事件。检查速度/活动。您可以在不启用后台服务的情况下执行此操作。
  • 如果开车只是保存位置。开车时您会频繁更换,因此您只会落后几分钟。
  • 不开车或怠速,根据您的精度要求创建一个合理大小的区域。只要设备在该地区,您就可以使用当前保存的最近位置。这将是整个 24 小时内的大部分时间,因此您对电池的总体影响很小。

在我看来,您的观点是错误的。认为您将发送消息然后通过让 CL 做某事来响应是合乎逻辑的。这不是服务的预期方式。本质上,您设置了位置管理器并放手。

查看我在 GitHub 上的存储库中使用的一些模式,您可能会受益。

TTLLocationHandler

它已经一年没有更新了,我不记得我在它上面学到的所有东西,但它对我很有帮助,因为我不必摆弄它。

按照该示例,您将根据应用程序状态停止和启动,或切换到区域或重大位置更改监控。无需交互。

您的位置事件会在它们到来时进行处理,无论背景与否。

在您的远程通知响应中,您只会阅读最新的位置信息或您需要的任何信息,并按照您的意图行事。您不在这里调用 startUpdatingLocation。如果您在后台使用定位服务,您一开始就不应该停止它。

编辑-正确使用过期处理程序块

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    __block UIBackgroundTaskIdentifier bgTask =0;
    UIApplication  *app = [UIApplication sharedApplication];
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
          // Do something here. Perhaps you'll log the error or respond with some fall back
          // This block will only be run if the handler expires without having been ended
          // In that case, you need to end it now or you will crash
          If (bgTask != UIBackgroundTaskInvalid) {
              [app endBackgroundTask:bgTask];
              [bgTask = UIBackgroundTaskInvalid];
          }

      }];

      // This is where the code you intend to run in the background starts

     [self.locationManager startUpdatingLocation];

     // Now tell the system you are finished, ending background task normally before exit

     If (bgTask != UIBackgroundTaskInvalid) {
         [app endBackgroundTask:bgTask];
         [bgTask = UIBackgroundTaskInvalid;
     }
}
于 2014-02-07T11:54:47.820 回答
0

该方法beginBackgroundTaskWithExpirationHandler:使应用程序有时间处理它的内容,但这并不意味着该任务可以永远运行。
如崩溃日志中所述,您的应用程序已被杀死,因为您显然已经用完了时间。
您应该得到相同的结果,只是要求更新位置而不包装到过期处理程序中。
我记得在旧系统中,我通过使用区域监控在后台触发位置更新来管理类似的东西。
[编辑]
为了在使用位置时获得最佳结果,我建议您使用真实设备,也许您是。

于 2014-02-07T11:53:42.337 回答