Android 上的 BLE(低功耗蓝牙),创建并重新连接到并不总是存在的设备

chk*_*ksr 2 disconnect bluetooth-lowenergy

嗯,我想连接到带有 BLE 的 OBD2 加密狗,它根据汽车状态打开/关闭。按照目前的计划,Android 设备本身将一直运行。

据我所知,重新连接有两种选择:

a) 将 connectGatt 'autoconnect' 参数设置为 'true'

b) 定期扫描设备,直到出现“我的”MAC,然后连接到该设备

我的问题:

  • “自动连接”有多可靠?

  • 是否有更好的方法(例如,每当出现新的 BLE 设备时进行系统广播等)来减少扫描并立即打开设备,或者至少开始扫描?

另外

哪个 Android 版本对于此类任务来说足够可靠?我读到 BLE 实现一开始并不可靠。

Emi*_*mil 6

“自动连接”和扫描+“直接连接”之间有一些区别。理想情况下,“自动连接”是我们想要的,因为它只需要一个广告数据包来建立连接,而不是两个。

扫描参数

自动连接、直接连接和扫描在收听广告时都使用不同的扫描参数。您使用的模式和广告间隔会严重影响连接设置时间。

  • 自动连接在所有非古代 Android 版本中每 1280 毫秒使用 48 毫秒的窗口,据我所知,所有供应商都在使用自动连接。当广告设备的广告间隔较短(例如 20 毫秒)时,此方法非常有效。我想你的车在启动时可以爆发出这么短的广告间隔,如果一段时间没有建立连接,可能会增加间隔。当使用 20 毫秒的广告间隔时,应在 1280 毫秒内建立连接,因为在 Android 设备打开无线电的 48 毫秒内至少应捕获一个数据包。然而,如果您以 1 秒的间隔进行广播,则可能需要很长时间才能连接,因为广播必须恰好在 1280 毫秒的 48 毫秒时间范围内发送。

  • 直接连接使用每 60 毫秒 30 毫秒的窗口来快速建立连接。然而,直接连接会在 30 秒后超时,并在onConnectionStateChange回调中发送错误状态。因此,此模式只能在用户按下“连接”按钮的 GUI 中使用,或者在运行某些扫描之后并且您想要连接到在扫描期间找到的设备时使用。

  • 正常扫描操作使用完全不同的扫描窗口和扫描间隔。历史上,1 秒扫描窗口和 5 秒扫描间隔用于“低功耗”,5 秒扫描窗口和 5 秒扫描间隔用于“低延迟”。在Android的更高版本中,这种情况变得更糟,例如扫描间隔为15或45秒,而窗口并没有增加。

处理意外停车

随着时间的推移,Android 上的后台扫描变得越来越严格,您需要小心扫描设置、扫描过滤器、扫描频率和时间,以确保 Android 设备真正扫描您的 BLE 设备。幸运的是,Android 8 中添加了使用 Pending Intents 的新 API,让系统可以扫描您的设备,即使您的应用程序已被系统终止以节省内存。您应该阅读http://www.davidgyoungtech.com/2017/08/07/beacon-detection-with-android-8,它比较了一堆扫描选项。然而,该文章似乎没有提到当您有前台服务时的情况(这需要通知栏中有一个图标向用户表明您的应用程序正在运行),这大大降低了所有限制。

然而多年来,根本没有添加对“自动连接”的限制。我知道的唯一“限制”是您的应用程序进程中应该有一个前台服务,以避免应用程序被杀死,从而丢失您的连接尝试。

稳定

当然,Android 的蓝牙堆栈中存在大量错误,其中一些已修复,另一些仍然存在。还有一些预期行为会导致不稳定 - 例如,当 Android 认为您扫描过多时,它会停止您的扫描。

无论您使用哪种方法,如果您希望在用户关闭和打开蓝牙后(或者蓝牙堆栈崩溃并重新启动)保持连接,您需要有一个 BroadcastReceiver 来侦听重新启动BluetoothAdapter.ACTION_STATE_CHANGED所有挂起的连接/扫描。您可能还需要设置一个 BroadcastReceiver 来侦听系统何时启动或您的应用程序更新,以便在这些情况下启动您的应用程序,以便您可以启动挂起的连接。

也许这与您的情况不太相关,但无论您使用哪种连接方法,您还应该意识到 Android 对最大连接数、BlueoothGatt 对象、L2CAP 链接以及可能导致通信以意外方式失败的内容有限制。有时是由于onConnectionStateChange回调中出现错误,有时是由于没有回调而达到了限制。

对于“自动连接”,你真的应该实现类似https://github.com/Polidea/RxAndroidBle/blob/7663a1ab96605dc26eba378a9e51747ad254b229/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/BleConnectionCompat.java的东西,如果您知道您的设备运行的是 Android 6 或更低版本,因为https://issuetracker.google.com/issues/36995652会导致选择“直接连接”而不是“自动连接”。

对于“自动连接”,我编写了一个重要的补丁https://android.googlesource.com/platform/system/bt/+/8451ad010e26562d4a4c7d0d135c70c4325ee6b5,该补丁在Android 8.1中被接受,该补丁早些时候导致蓝牙堆栈挂起,直到设备重新启动为止超时,然后在 Android 设备超时之前重新启动广告。

当您的代码在足够新的设备上运行以支持传递TRANSPORT_LEtoconnectGatt时,请使用该方法,否则 Android 有时会尝试使用经典蓝牙进行连接。

蓝牙设备地址

由于严重的设计缺陷,无法告诉蓝牙堆栈通过完整地址(地址类型随机/公共+地址)进行连接。仅当您想要获取对象BluetoothDevice(然后用于连接)时,才可以提供 48 位地址。通过深入研究Android蓝牙堆栈的源代码,发现蓝牙设备是通过48位地址而不是49位地址来识别的,这在任何地方都是同样的问题。他们的“快速解决方案”是向设备信息添加一个属性,指示设备是否具有公共地址或随机地址。该位不能由应用程序设置,只能在扫描期间设置。当连接connectGatt并且地址类型未知时,它会尝试使用公共地址类型建立(Android 7的一些子版本在使用“自动连接”时根据48位地址中的某些位对地址类型进行了一些愚蠢的猜测) ”)。如果您的 BLE 设备具有静态随机地址,则它将无法连接。通过执行扫描并检测到您的设备,它会将设备的地址和地址类型存储在 RAM 中的表中,因此,如果您稍后使用 来连接到它,connectGatt它将成功,因为现在使用了正确的地址类型。当蓝牙重新启动时,该表将被清除。请注意,如果您执行绑定,则设备信息将写入磁盘,包括地址类型,因此即使重新启动蓝牙,连接到绑定设备也应该始终有效。

概括

尽管存在上述所有问题,只要您遵循上述指南,“自动连接”通常会完美运行并且非常稳定,假设您的外设的广告间隔很短,这将为您提供快速的连接设置时间。在 Android 8.1 及更高版本中,大多数关键错误已得到修复。

您的第二个选择可能是使用待定意图扫描选项,当检测到您的设备时,您会从系统收到广播(即使这可能需要相当长的时间才能检测到您的设备)。当然,为了安全起见,您也可以使用这两种方法......