如何实现 JobScheduler 在后台扫描蓝牙信标?

How to implement JobScheduler to scan for Bluetooth beacons in the background?

我正在编写一个应用程序,当应用程序处于前台和后台时扫描蓝牙信标。我弄清楚了前景部分,但我不知道如何处理背景部分,尤其是在 android 8.0 及更高版本中,系统不允许应用程序 运行 超过 15 分钟背景。

该应用需要扫描信标并获取其 mac 地址和 UUID。此外,它应该得到扫描响应,因为那里有一些我需要解码和保存的信息。我已经使用指南 here 实现了使用 BluetoothLeScanner 的前台扫描。至于背景,我尝试将扫描模式更改为 LOW_POWER,但 OS 会在大约 15 分钟后终止应用程序。请注意,我不想要一个不断通知的前台服务,我可以只扫描 运行ning 间隔约 15 分钟。

许多人建议使用 Android Beacon 库,但我找不到我们使用的信标类型 Kontakt Beacon Pro BP16-3 所需的 beaconLayout,因此 Beacon 库无法检测到它们。

我需要的信标信息包括唯一 ID 和电池百分比。有关它们在扫描响应中所处位置的详细信息,请参阅[此处] (https://support.kontakt.io/hc/en-gb/articles/206294004-How-to-check-the-battery-level-on-your-beacons)。

我将不胜感激任何有关编写代码以在后台搜索信标的帮助,这些信标适用于 6.0 及更高版本的任何 Android 版本,或者帮助我将信标库与我上面提到的信标一起使用。

编辑:来自扫描响应的唯一 ID 和蓝牙

当我使用 BLEScanner 扫描信标时,我可以使用 ScanResult::getScanRecord() 来获取 scanRecord 对象。然后,我使用 getServiceData() 方法获取一个字节数组,其前 4 个字节表示 ASCII 中的唯一 ID,接下来的两个字节是 ASCII 中的固件版本,最后一个字节是十六进制的电池百分比。我什至用官方 Kontakt 应用程序确认了电池电量,所以我确信它是正确的。

当我使用 Beacon 库时,我找不到一种简单的方法来获取扫描响应的解析版本。相反,我必须使用 NonBeaconLeScanCallback 来获取字节数组。然后,字节数组变成了

[2, 1, 6, 26, -1, 76, 0, 2, 21, -9, -126, 109, -90, 79, -94, 78, -104, -128, 36, -68, 91, 113, -32, -119, 62, -91, 68, 124, 56, -77, 8, 9, 75, 111, 110, 116, 97, 107, 116, 2, 10, -12, 10, 22, 13, -48, 68, 106, 69, 77, 52, 50, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0].

似乎索引 46-49 中的字节表示 ASCII 中的唯一 ID,在本例中为 "DjEM"。此外,索引 52 处的字节是十进制的电池百分比;在本例中,它是 68。

看来,通过使用 BLE 扫描仪,我可以在解析唯一 ID 和电池方面省去很多麻烦。但是,可靠地实施后台扫描会困难得多。因此,有没有办法结合两者的优点,让 bacon 库解析唯一 ID 和电池百分比?

EDIT2:无法识别我的信标时的信标库消息

即使我同时使用 iBeacon 和 EddyStone 信标布局,信标库仍然无法检测到我的信标。它在 logcat:

中打印以下内容
 processing pdu type 16: 0201060d166afe0206010a64f456425a4d08094b6f6e74616b74000000000000000000000000000000000000000000000000000000000000000000000000 with startIndex: 5, endIndex: 16
This is not a matching Beacon advertisement. (Was expecting 02 15.  The bytes I see are: 0201060d166afe0206010a64f456425a4d08094b6f6e74616b74000000000000000000000000000000000000000000000000000000000000000000000000
Ignoring pdu type 01

提前致谢。

您无法仅通过扫描广告来读取 Kontakt.io 信标的电池电量。您必须使用板载 GATT 服务连接到它(更多内容见下文。)

对于后台和前台的一般扫描,您当然可以使用 Android 信标库。您可能想要使用 iBeacon 布局:

        mBeaconManager.getBeaconParsers().add(new BeaconParser().
                setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));

但是虽然上面会检测到你的信标,但它不会告诉你电池电量。那是因为 Kontakt 不会公布其电池电量。要获取它,您必须单独连接到 Service UUID = 0x1805 的 GATT 服务,然后读取 Characteristic UUID = 0x2a19 的特征值。这将 return 一个介于 0-100 之间的值,指示电池的百分比级别。

您可以在 Kontakt.io 的 "Other BLE Scanners" 部分 here.

中查看有关如何访问它的详细信息

好的,所以这并没有告诉您如何编写代码来读取该值。为此,您需要学习蓝牙 LE GATT 编程的基础知识,这需要一点学习。一个好的起点是 here. 您需要编写几个步骤:

  1. 扫描设备(Android Beacon 库使用其 Ranging API 为您完成此部分,为您提供 String macAddress = beacon.getBluetoothAddress(); 中的设备 MAC 地址)
  2. 连接到设备。如果您已使用该库进行发现,则可以获得对设备的引用并使用 bluetoothAdapter.getRemoteDevice(macAddress).connectGatt(...)
  3. 连接到它
  4. 发现服务
  5. 在上述步骤的服务列表中查找 0x1805 服务 uuid。
  6. 从上面发现已发现服务的特征
  7. 在上述步骤的特征列表中查找 0x2a19 特征 uuid。
  8. 读取上述发现特征的值。

使用附近信标的蓝牙 MAC 地址字符串的存储副本,可以将上述所有内容放入每 15 分钟 运行 的预定作业中。

我知道这并不容易。我希望我能给你几行代码来获得电池电量,但不幸的是它不能那样工作。欢迎来到蓝牙 LE GATT 编程世界!

根据链接的 Kontakt.io 文档,电池电量也可在扫描响应中找到。 (注意:当您检测到信标数据包时,扫描响应并非 总是 可用,但它通常可用 - 它由 Android OS 和当收到扫描响应时与扫描数据合并。)在原始 Android 扫描结果 bye 数组中,扫描响应简单地附加在常规扫描数据的末尾。

当使用Android Beacon 库时,扫描响应也是可用的,Android 操作系统获取扫描响应并将其存储在BluetoothDevice#name 字段中。 (参见 here)。 Android Beacon 库在解析信标时,将该字段复制到 Beacon#name 字段中。因此,如果您可以解析信标并且设备检测到扫描响应,则该信息将以字符串形式提供给您。

这里有两个障碍:

  1. 您的 Kontakt.io 信标似乎没有宣传任何实际上是信标广告的内容。您可能需要将其配置为通告 iBeacon 格式或 Eddystone-UID 格式。一旦你这样做并使用该布局配置 Android 信标库,它就会检测到它。请注意,显示的字节:processing pdu type 16: 0201060d166afe0206010a64f456425a4d08094b6f6e74616b74000000000000000000000000000000000000000000000000000000000000000000000000 with startIndex: 5, endIndex: 16 This is not a matching Beacon advertisement. 不对应 iBeacon 或 Eddystone。这似乎是某种专有的 16 位 GATT 服务广告(AD 类型 0x16)。它有一个 0x0102 的 16 位服务 UUID,它对应于 standard or custom 16 位 UUID 的蓝牙 SIG 列表中的任何内容。你的猜测和我的一样好!

  2. BluetoothDevice#name 或 Beacon#name 将是一个字符串。您需要将其转换为字节,然后按照 Kontakt.io 在其扫描响应文档中的描述解析出电池电量。