【问题标题】:(iOS) Multiple notifications via locationManager:didExitRegion: when exiting a region(iOS) 通过 locationManager:didExitRegion 的多个通知:退出区域时
【发布时间】:2012-03-07 13:54:51
【问题描述】:

我正在开发一个使用 CLLocationManager 区域监控的基于位置的应用程序。

我正在使用单个 CLLocationManager 和单个委托(在启动时在主应用程序委托中设置),我注意到我经常收到对我的委托的多次调用(在 locationManager:didExitRegion: ) 退出受监控区域时——通常是两个调用,但有时更多。有没有其他人经历过这种情况,或者有任何想法可能出了什么问题?

我在应用委托中实例化的类中按如下方式实例化 CLLocationManager:

    _locationManager = [[CLLocationManager alloc] init];
    _locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    _locationManager.delegate = self;

我正在像这样设置区域监控:

    // The region instance has a radius of 300 meters
    [_locationManager startMonitoringForRegion:region desiredAccuracy:1000];

据我从documentation 了解到,提供 1000 的所需精度意味着 locationManager:didExitRegion: 仅应在我们在该区域外 1000 米时调用。

另外一点 - 据我所知,如果我在车里(因此旅行速度很快),我只会收到多个通知。如果我骑自行车或步行,这似乎不会发生。任何关于我做错了什么的指针(或者如果这是其他人已经遇到的问题),我们将不胜感激。

【问题讨论】:

  • 仅供参考,您可以在模拟器上模拟您的位置。您甚至可以模拟旅行路径。不用开着车到处乱跑^^
  • github.com/futuretap/FTLocationSimulator 使您能够将 Google 地球 KML 路线文件加载到模拟器中 :)
  • 关于模拟位置,是的,我知道这是可行的,但是由于在硬件上使用蜂窝塔等,模拟器上的区域监控与真实硬件上的区域监控有很大不同。我已经在模拟器上进行了大部分测试,但有些问题(如上面的问题)只能在硬件上进行真正的测试。

标签: iphone ios cocoa-touch core-location


【解决方案1】:

我敢打赌这是你的问题,locationManager:didExitRegion: 会在你 iPhone 上每个应用程序中的每个位置管理器注册的每个区域都被调用。您需要将作为参数发送的区域标识符字符串与您希望应用当前执行操作的区域的区域标识符字符串进行比较。

当您发送[_locationManager startMonitoringForRegion:region desiredAccuracy:1000]; 时,您正在创建一个区域,如果该区域尚不存在进行监控。这与向 NSNotification Center 添加观察者不同。 Apple 的区域对象盲目地向每个 CLLocationManager 发送通知,然后将消息发送给代理。

【讨论】:

  • 有趣的理论,但我很确定你说 locationManager:didExitRegion: 被每个应用程序上的每个区域调用是不正确的——正如文档 (goo.gl/TvU7y) 所说,区域是系统范围的资源,因此是有限的;但是,只有在同一个应用程序中有多个 CLLocationManager 时,您才能收到多个通知。此外,检查区域的标识符属性旨在代替指针级别的相等检查,但不是为了确保您没有收到应用程序外部通知。
  • 你是对的,它只在活动的 LocationManagers 中被触发。但是,每次退出 SYSTEM 中的任何区域时都会触发它。我敢肯定,如果您创建两个应用程序,为某个区域运行一个 startMonitoring,退出该应用程序,启动另一个为另一个区域启动监视器的应用程序,则活动的 LocationManager 将向其代表发送消息,用于两个区域(以及其他任何其他区域)应用程序可能已注册)。
【解决方案2】:

首先,文档并没有表明在 didExitRegion 事件生成之前,desiredAccuracy 单独决定了您需要离开区域多远。如果您想微调触发事件的频率,您还需要使用 distanceFilter,它确定在触发事件之前您需要移动的水平移动。

如果使用 distanceFilter 不起作用,那么我会推荐以下方法:

  1. 如果您使用的是 NSNotificationCenter,请确保您已通过 [[NSNotificationCenter defaultCenter] removeObserver:self] 从通知中删除其他类。您可以选择为此调用指定名称和对象。

  2. 1000 公里是一个大半径,与附近的许多区域相交的可能性很高。我会为此参数尝试一个较小的数字,看看这是否不会减少您收到的退出通知的数量。唯一表明这可能不是解决方案的是您没有说您也收到了 didEnterRegion 的爆炸。

  3. 最后,我会检查在 didExitRegionEvent 中传递的 CLRegion 的标识符,看看您是否不能自己设置区域的 NSDictionary。您必须在 didEnterRegion 上为字典设置一个区域,并在 didExitRegion 上将其删除。因此,在 didExitRegion 上,您所要做的就是通过检查您是否已经拥有该区域来确保您对该区域感兴趣。我猜想 CLRegion 已经配备了 isEqual: 和 hash 以允许将其存储在集合中。

祝你好运!

【讨论】:

  • 感谢您的想法。然而,关于距离过滤器,我只是检查了文档,我没有看到距离过滤器和区域监控之间的任何(记录的)关系。我没有在这里使用 NSNotificationCenter,所以不可能。顺便说一句,半径以米表示,而不是公里,所以我们在这里处理 1 公里。关于存储 CLRegions,文档指定您只能依赖标识符来实现相等性。
  • 有什么理由不能在 NSDictionary 中使用标识符吗?保留区域地图可能看起来有点尴尬,但如果你管理大小并适当地释放它,它应该可以解决问题。
【解决方案3】:

我认为你最好使用小于 300m 半径的所需精度,即kCLLocationAccuracyHundredMeters。你试过吗? 没有关于基本逻辑的文档,但我假设 desiredAccuracy 可以被视为您必须经过的 minimium 距离,该移动算作“过境点”。
区域监控基于“重要位置事件”,而不是 GPS——否则电池无法使用半天。

如果您使用如此高的desiredAccuracy,系统可能会收到不止一个重要的位置事件(这些事件似乎大约每 500m 生成一次 - 也取决于您在该区域拥有多少 wifi 网络。 在这种情况下,系统可能会将由显着变化产生的当前位置与到您所在区域四周的距离进行比较。 如果您距离您所在区域的另一侧仅 1000m 之外,它可能会再次通知,并且只有在您距离您所在区域的每一侧 1000m 之外时才停止通知。

accuracy 参数的原因是为了避免过境,如果您离边界太近,那么您在边界外旅行时看不到内外等...

在我的应用程序中,我尝试了许多不同的半径和精度组合,最终选择了 500m/100m 和 100m/10m——这些在我的城市场景中的实际工作中非常有效。

(另见优秀文章系列http://longweekendmobile.com/2010/07/22/iphone-background-gps-accurate-to-500-meters-not-enough-for-foot-traffic/

【讨论】:

  • 有趣的想法...我肯定会尝试一下(当然,如果确实如此,我会支持/接受)。
【解决方案4】:

tl;dr:这真的很简单——你会得到尽可能多的信息,说明你正在跨越手机信号塔的边界——这意味着,根本就不是很好的数据。

现在是真实的故事:

一般来说,CoreLocation 可以通过两种方式确定一个人的位置:

  • 通过 GPS。这是非常准确的(几十米),但使用起来很耗电。
  • 通过 WiFi。这通常是准确的,尽管 wifi 基站可以并且确实会改变,这使得它变得模糊。此方法交叉引用该区域中的 wifi 站点与一些已知的准确位置 - 因此它知道何时看到“FooWifiStation”它与特定区域相关联,由精确仪器测量,甚至可能是其他打开 GPS 的手机(其中是有争议的,我们可能永远不知道苹果是否使用这种方法)
  • 按手机信号塔位置。这些不会移动,因此当您与塔相关联时,它知道您处于一个大的模糊覆盖点内。这是最不准确但最省电的方法,因为您的手机已经在做保持联系的工作。

如果您“冷”地进入地图应用程序,您甚至可以看到这一点:您会立即从一个大的模糊蓝点开始,(至少在我所在的 1 公里处),然后它会随着 wifi 位置修复而缩小,然后当 GPS 得到修复时,它或多或少地消失了。它正在实时执行 [cell tower] => [wifi] => [gps] 准确度。

根据我的经验,“显着位置变化”条款的意思是“只要我们不需要做更多的工作或花费更多,我们就会在您搬家时通知您,获取数据的能力。这意味着事实上,你可以依赖的最好方法是使用信号塔之间的转换。Apple 故意让这个模糊不清,因为如果另一个应用程序使用具有更好分辨率的东西 - 例如,你打开 Maps.app,它会获得 GPS 定位 - 您可能会突然在位置上获得出色定位,但您不能总是如此。您已经问过了对于位置的“弱参考”。

现在想想当你在车里闲逛时会发生什么:你的手机必须管理这种转变。它被移交,一次与多个塔对话,诸如此类的事情,以管理无缝过渡,这在您进行对话时必须是可行的。对于任何小盒子来说,这都是一项非常出色的壮举。必然地,这会导致您在位置更新中看到一些反弹,因为对于手机而言,此时您正在蜂窝塔之间振动,因为它正在协商过渡。

因此,半径的真正含义是您对大致具有该精度的数据感兴趣 - 但 API 不会 保证您在该半径内获得更新。你可以把它想象成苹果公司说:“我们将把你的准确度分为 3 组之一——但我们没有给你一个枚举,因为我们希望保留开发更好的方法来修复你的位置的权利,而无需您必须更改代码”。当然,实际上,任何真正的应用程序如果改变获取位置的方法都会改变,因为他们恼人地对这些位置信息含糊其辞。

换句话说,您将获得蜂窝塔的位置修复,并完全猜测该准确度有多好;当您移动到下一个塔时,您会立即跳到 它的 位置,并进行类似的模糊修复 - 这有意义吗? CoreLocation 告诉您您在信号塔,其准确度与信号塔的信号到达的距离一样;当你移动到另一个塔时,你可能会得到切换的弹性。

所以说到它,要真正做好工作,你必须假设数据是“好”、“好”或“坏”之一,并使用其他方法——比如Kalmann filters——如果你真的需要更好地猜测用户在哪里。作为一个零阶近似值,我将根据给定的更新的 时间 对回调进行去抖动,并假设用户在几秒钟内并没有真正来回跳跃公里,而是朝着第一个新更新的方向前进。

【讨论】:

    【解决方案5】:

    我发现为了节省电池您必须使用monitorForSignificantLocationChange。 我的解决方案是过滤掉同一地区 60 秒内出现的多个警报:

    -(BOOL)checkForMultipleNotifications:(GeoFenceModel*)fence
    {
      GeoFenceModel *tempFence = [fenceAlertsTimeStamps objectForKey:fence.geoFenceId];
      if(tempFence == nil){
        fence.lastAlertDate = [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]];
        [fenceAlertsTimeStamps setObject:fence forKey:fence.geoFenceId];
        NSLog(@"checkForMultipleNotifications : no Notifications found for Region : %@, Adding to Dictionary with timestamp %.1f",fence.geoFenceId,fence.lastAlertDate.doubleValue);
      }
      else if(([[NSDate date] timeIntervalSince1970] - fence.lastAlertDate.doubleValue) <= 60.0){
        NSLog(@"checkForMultipleNotifications : Multiple region break notifications within 60 seconds, skipping this alert");
        return NO;
      }
      return YES;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 2013-04-23
      • 2014-08-01
      • 1970-01-01
      • 2016-04-29
      • 2015-06-27
      • 2015-10-17
      相关资源
      最近更新 更多