BLE(蓝牙低能耗)在安卓系统上,创建并重新连接到并不总是存在的设备



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

AFAIK我有两个重新连接的选项:

a) 将connectGatt"autoconnect"参数设置为"true">

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

我的问题:

  • "自动连接"的可靠性如何?

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

此外还有

哪个版本的安卓系统对于这样的任务来说足够可靠?我读到BLE的实现在一开始并不可靠;)

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

扫描参数

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

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

  • 直接连接使用大约每60毫秒30毫秒的窗口来建立快速连接。但是,直接连接在30秒后超时,并在onConnectionStateChange回调中发送错误状态。因此,该模式只能在用户按下"连接"按钮的GUI中使用,或者在扫描运行后,您希望连接到扫描过程中找到的设备。

  • 正常的扫描操作使用完全不同的扫描窗口和扫描间隔。历史上,1秒扫描窗口和5秒扫描间隔用于"低功耗",5秒扫描窗口和五秒扫描间隔则用于"低延迟"。在后来的安卓版本中,情况变得更糟,比如扫描间隔为15或45秒,而窗口没有增加。

处理意外停车

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

然而,多年来,对"自动连接"没有任何限制。我知道的唯一"限制"是,你的应用程序进程中应该有一个前景服务,以避免应用程序被杀死,从而丢失你的连接尝试。

稳定性

当然,安卓的蓝牙堆栈中有很多漏洞,其中一些已经修复,有些仍然存在。还有一些故意的行为会导致不稳定,比如当安卓认为你扫描太多时就会停止扫描。

无论您使用哪种方法,如果您希望在用户关闭和打开蓝牙后(或者如果蓝牙堆栈崩溃并重新启动)保持连接,则需要有一个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,这在安卓8.1中被接受,早期导致蓝牙堆栈挂起,直到设备超时时重新启动,然后在安卓设备超时前重新启动广告。

当你的代码在一个足够新的设备上运行,支持将TRANSPORT_LE传递到connectGatt时,请使用该方法,否则Android有时会尝试使用蓝牙经典连接。

蓝牙设备地址

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

摘要

尽管存在上述所有问题,但只要您遵循上述指导原则,"自动连接"通常可以完美工作,并且非常稳定。假设您的外围设备的广告间隔很短,这将为您提供快速的连接设置时间。在Android 8.1及以上版本中,大多数关键错误都已修复。

您的第二个选择可能是使用Pending Intent扫描选项,当您的设备被检测到时,您可以从系统中获得广播(即使这可能需要很长时间才能检测到您的设备)。当然,为了安全起见,你也可以同时使用这两种方法。。。

这个问题更适合超级用户网络,因为这不是编程问题。

回答您的问题:

"自动连接"的可靠性如何?

如果你知道如何使用自动连接,它是可靠的它适用于当今大多数安卓系统。您通常应该使用Android7及更高版本以使其正常工作。有些人可能会说安卓6足够了。我不这么认为。有一个已知的种族状况和变通方法(来自Polidea的家伙),但你永远不知道补丁是否有是否已应用(如果您不是自己编程)。

自动连接仅适用于缓存绑定设备!否则就没有机会了。它现在普遍有效。

为什么人们认为这很难,甚至不起作用?如果你想使用/编程它,前面有一些障碍需要解决。

  • 谷歌的文档非常简单,往往缺乏细节甚至关键信息
  • 考虑到竞争,安卓BLE的api级别较低
  • 每个供应商都被允许对BLE堆栈进行更改,这会导致不兼容。当某个东西在一部手机上工作时,并不意味着它会在不同制造商的第二部手机上自动工作
  • 代码中有很多错误。最差的是4.5&6,其他人过得更好,但仍然有一些错误,没有任何解决办法。我们都喜欢神秘的虫子133。建议使用Android 7或更高版本

有没有更好的方法(例如,每当出现新的BLE设备时,系统广播或类似的方法)来减少扫描并立即打开设备,或者至少在那时开始扫描?

您有两个选项:

  1. 首先进行主动扫描,然后使用设置为false的自动连接进行连接。比自动连接更快。

  2. 自动连接设置为true-这比1需要更长的时间。因为Android使用低能耗设置进行扫描。自动连接所需的连接时间因供应商而异。基于使用的低能量设置(和实现)

自动连接的一大优点是它可以同时发出多个连接。自动连接为false时,您可以一次发出一个连接。

使用的材料:主要是Martijn van Welie的博客《让Android BLE工作——第1部分》和《让Android BLE工作——第2部分》(我建议阅读这些以了解编程细节!)

最新更新