(iOS) iBeacon 唤醒是如何工作的?

(iOS) How does waking up by iBeacon work?

我正在使用 iBeacon 进行测试,以便在 iOS 应用程序被杀死后执行一些与蓝牙相关的任务。

其实效果很好,不过还是很好奇效果如何

这是我使用的代码。

private func startMonitoring() {
    if CLLocationManager.isMonitoringAvailable(for: CLBeaconRegion.self) {
        self.log("startMonitoring")
        let region = CLBeaconRegion(...)
        self.locationManager.startMonitoring(for: region)
    }
}

func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    if let region = region as? CLBeaconRegion {
        if CLLocationManager.isRangingAvailable() {
            self.log("didEnterRegion")
            self.locationManager.startRangingBeacons(satisfying: region.beaconIdentityConstraint)
        }
    }
}

func locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {
    if !beacons.isEmpty {
        self.log("didRange")
        self.doSomething()
    }
}

我在应用程序最初启动时调用了一次 startMonitoring(),并使用了两个 CLLocationManagerDelegate 方法。我还添加了 log() 进行测试。

我预计在我终止应用程序后,会先调用 didEnterRegion,然后调用 didRange,最后执行任务。

但事实证明,我只看到 3 个关于“startMonitoring”的日志,这意味着(我猜)iBeacon 以某种方式调用了 startMonitoring()

怎么可能?为什么应用程序不调用委托方法,为什么它甚至运行良好?

基于信标检测启动应用程序在 iOS 上运行良好,因为 信标监控建立在与地理围栏区域监控相同的 CoreLocation 框架功能之上。它是这样工作的:

  1. 当您的应用程序注册一个区域进行监控时,操作系统会记住您的应用程序和该区域,并将这对添加到 OS 级跟踪列表中。
  2. 每当 iOS 检测到位置变化(lat/lon 用于 CLCircularRegion 监控或 BLE 广告数据包用于 CLBeaconRegion 监控),它会将变化与此跟踪列表进行比较。
  3. 如果检测到状态变化,iOS 会检查应用程序是否 运行。如果是这样,它会根据需要调用 didEnter 或 didExit 委托方法。
  4. 如果应用不是 运行,它首先将应用 启动到后台,调用应用委托的 onCreate 方法。 didFinishLaunching returns 之后,iOS 检查触发启动的区域状态更改是否已向 CoreLocation 注册。如果是,它调用 didEnter 或 didExit。

第 4 步中的顺序对于完成这项工作至关重要 — 如果您在应用委托 didFinishLaunching 结束之前重新开始监视,您将获得 didEnter 回调。

而且,是的,即使在从任务切换器中终止应用程序后,这一切都有效,因为 iOS 在终止应用程序时不会删除应用程序的监控区域。这是您可以在该操作后重新启动和应用的少数几种方法之一。

如果您没有看到与上述一致的日志行,则您的日志记录可能存在问题。尝试设置断点,您将看到按照我上面描述的顺序进行的调用。

有关 Apple 对 CoreLocation 更改启动应用程序时如何调用 didFinishLaunching 的描述,请参阅 this page。该页面专门用于显着位置更改服务,但同样的机制适用于信标监控:

If you start this service and your app is subsequently terminated, the system automatically relaunches the app into the background if a new event arrives. In such a case, the options dictionary passed to the application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods of your app delegate contains the key UIApplicationLaunchOptionsLocationKey to indicate that your app was launched because of a location event. Upon relaunch, you must still configure a location manager object and call this method to continue receiving location events. When you restart location services, the current event is delivered to your delegate immediately. In addition, the location property of your location manager object is populated with the most recent location object even before you start location services.