Monitoring BeaconRegion Immediately 还有其他解决方案吗?

Are there any other solutions in Monitoring BeaconRegion Immediately?

我用 iBeacon 做了一个申请。
我决定使用 CoreLocation 和 Ranging。
但是,我觉得测距太耗电了。

作为 Ranging 的替代品,我尝试使用 Monitoring。
然后,我分析了 Ranging 的使用能量水平和 Monitoring 的使用能量水平。

测距比监控多使用约 2 个能源使用水平
结果:(测距等级:10/20,监测等级:8/20)

但是,监控不会立即调用 didExit 或 didDetermineState
我希望我的应用程序具有实时测距功能。


我的解决方案:

  1. 监控开始
  2. 如果我进入监控区域,我开始测距
  3. 如果我退出区域,测距结果为零,并停止测距
  4. 监控停止和启动。

监控不会立即调用 exit 或 determineState 方法。
但是,我发现停止并重新运行监控使我的应用程序具有实时性。

我认为该解决方案可减少待机时的能耗。
它真的有效!

▼ 这是我的代码。

class Service: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, CLLocationManagerDelegate{
    private let constraint = CLBeaconIdentityConstraint(uuid: Constants.beaconUUID!,
                                                        major: Constants.beaconMajor,
                                                        minor: Constants.beaconMinor)
    
    private let region = CLBeaconRegion(beaconIdentityConstraint:
                                            CLBeaconIdentityConstraint(uuid: Constants.beaconUUID!,
                                                                       major: Constants.beaconMajor,
                                                                       minor: Constants.beaconMinor),
                                        identifier: Constants.beaconIdentifier)

    
    var locationManager: CLLocationManager!

    override init() {
        super.init()
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()
    }
}

extension Service {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if manager.authorizationStatus == .authorizedAlways {
            region.notifyOnExit = true
            region.notifyOnEntry = true
            region.notifyEntryStateOnDisplay = true
            
            manager.startMonitoring(for: region)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
        if state == .inside {
            didEnterEvents(manager)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {
        if beacons.first == nil {
            didExitEvents(manager)
            stopAndRerunMonitoring(manager, for: region)
        }
    }
}

extension Service {
    private func stopAndRerunMonitoring(_ manager: CLLocationManager, for region: CLRegion) {
        print("reboot!")
        manager.stopMonitoring(for: region)
        manager.startMonitoring(for: region)
    }
    
    private func didEnterEvents(_ manager: CLLocationManager) {
        print("inside")
        manager.startRangingBeacons(satisfying: constraint)
    }
    
    private func didExitEvents(_ manager: CLLocationManager) {
        print("outside")
        manager.stopRangingBeacons(satisfying: constraint)
    }
}

我知道我的解决方案很糟糕。
但我找不到任何其他解决方案。
Plz,你能找到其他更好的解决方案吗?

等等:

  1. didEnter 和 didExit 调用了 determineState 方法。
    我需要在其他代码中调用 requestState() 方法。所以我在 determineState 方法中写了一个 Enter event logic
  2. 您需要实时?
    是的,因为我会用信标制作安全系统。所以我的应用程序的要求是实时的。 我需要实时区域和更少的能源使用。

您描述的在区域进入时开始测距并在区域退出时停止测距的方法并不“糟糕”。事实上这很常见。它通常用于确定检测到的信标的确切标识符,否则仅通过监视是不可能的。

对于您的用例,在区域内进行测距也确实会加快区域退出事件,因为它会强制进行持续的蓝牙扫描,而不是依赖用于监控的较慢的硬件过滤器。当测距处于活动状态时,区域退出会在最后一次检测到信标后 30 秒出现。

要使该解决方案切实可行,您必须考虑后台行为:

  1. 除非您使用特殊技巧,否则 iOS 在屏幕关闭时不会让您的射程超过几秒钟。一旦测距停止,您将失去加速区域出口。
  2. 当屏幕打开时,不一直测距以节省电池没有意义,因为发光屏幕使用的电池是持续蓝牙扫描测距的 100 倍以上。

要从您描述的技术中获益,您必须按照我的博客 post here 中的描述在后台延长测距时间。请注意,自从我写了那篇文章后,Apple 已将您可以在后台扩展范围的时间从 180 秒减少到 30 秒,这可能不足以满足您的需求。您可以获得无限的背景范围:

  1. 将位置背景模式添加到Info.plist
  2. 获得用户“始终”的位置许可。仅获得“使用时”权限是不够的。
  3. 按照此处所述启动后台任务:http://www.davidgyoungtech.com/2014/11/13/extending-background-ranging-on-ios
  4. 从 CoreLocation 请求 3 公里的位置更新。这将使应用程序 运行 保持在后台,而不会从 GPS 消耗额外的电池电量,因为 3 公里的精度仅使用手机无线电您无需对这些结果执行任何操作。您只需要请求他们让应用程序保持活动状态。