【问题标题】:How to implement JobScheduler to scan for Bluetooth beacons in the background?如何实现 JobScheduler 在后台扫描蓝牙信标?
【发布时间】:2019-03-31 21:41:15
【问题描述】:

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

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

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

我需要的信标信息包括唯一 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 和电池方面省去很多麻烦。但是,可靠地实现后台扫描会困难得多。因此,有没有办法将两者中最好的结合起来,并让培根库来解析唯一 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

提前致谢。

【问题讨论】:

  • 您的时间安排是否灵活?如果是,那么您可以尝试 WorkManager 和 PeriodicWorkRequest 之类的东西。即使应用程序未处于活动状态,它们也可以保证运行,如果您将间隔设置为 15 分钟,那么它将在 15 分钟左右触发。
  • @Ryujin ble 扫描器将能够扫描每个信标或仅扫描与过滤器匹配的信标。换句话说,如果我给一个完整或空的扫描过滤器,它会在后台运行时扫描每个 ble 设备吗?谢谢。
  • 我认为将 null 传递给 scanfilter 可以做到这一点。但这已经与您的主要问题不同了。
  • @Ryujin 这是相关的,因为如果 PeriodicWorkRequest 允许我在后台扫描任何信标,那么这就回答了我的问题。另外,我可以在任何 Android 操作系统版本上使用 PeriodicWorkRequest 吗?
  • WorkManager 是一个通用的后台作业 API,它不仅适用于 BLE。它一直被移植回 API 级别 14。请参阅 - developer.android.com/topic/libraries/architecture/workmanager

标签: android bluetooth-lowenergy ibeacon android-bluetooth ibeacon-android


【解决方案1】:

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

对于后台和前台的一般扫描,您当然可以使用 Android Beacon 库。您可能想使用 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 的特征值。这将返回一个介于 0-100 之间的值,指示电池电量的百分比。

您可以在 Kontakt.io 的“其他 BLE 扫描仪”部分 here 中查看如何访问它的详细信息。

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

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

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

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

【讨论】:

  • 感谢详细的回复!在我附加的链接中,它说电池电量和唯一 ID 包含在响应数据包中。事实上,当我在beacon库中打开debug模式并查看logcat时,我可以看到beacon的字节响应以及库如何不知道如何解析它。当我将该字节响应转换为 ASCII 时,我可以在其中看到唯一 ID。这是否意味着我仍然需要使用 GATT?
  • 另外,如果我使用作业调度程序,后台扫描会扫描区域中的每个信标还是仅扫描信标?自己实现后台扫描是否容易,还是太麻烦,我应该只使用信标库。
  • 您能否将扫描结果(带有扫描响应)粘贴到您的问题底部以及您认为的电池电量说明?
  • 自己实施后台扫描对于一种设备型号来说很容易,但要在所有设备上可靠地工作并不容易!我在库上做了大量工作以使其尽可能可靠,但我仍然被一些设备绊倒。如果你最终不得不做 GATT 我会让图书馆进行扫描,将蓝牙 MAC 保存到共享首选项,然后使用你的作业来处理最后看到的设备——从共享首选项中读取 MAC,通过 GATT 连接到每个设备并读取电池电量,然后清除 MAC 的共享首选项。
  • 我编辑了我的帖子。此外,信标库仍然没有检测到我的信标。我在我的帖子中添加了它打印的消息。
【解决方案2】:

根据链接的 Kontakt.io 文档,电池电量也可用于扫描响应。 (注意:当您检测到信标数据包时,扫描响应并非始终可用,但它通常可用 - 它由 Android 操作系统提供,并在扫描响应获取时与扫描数据合并已收到。)在原始 Android 扫描结果再见数组中,扫描响应只是简单地附加在常规扫描数据的末尾。

使用 Android Beacon Library 时,扫描响应也是可用的,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,它对应于蓝牙 SIG 列表中的 standardcustom 16 位 UUID。你的猜测和我的一样好!

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

【讨论】:

  • 似乎由于字节数组中的零,getName() 方法只返回出现零之前的第一部分。有没有办法使用信标库获取扫描记录?我更喜欢使用信标库的后台模式,而不是自己实现。
  • 目前 Android 信标库不直接解析扫描响应(它依赖于上述操作系统)。当然可以添加此功能,我认为这不会非常困难。如果您有兴趣将其作为对开源库的拉取请求来处理,我可以帮助您开始。或者,如果您可以让 Kontakt.io 信标发布库可以解码的 iBeacon、AltBeacon 或 Eddystone 标准信标,并且扫描响应为结束了。
  • 如果您想继续使用上述选项之一,我建议您在此处打开一个问题:github.com/AltBeacon/android-beacon-library/issues,这可能是一个更容易讨论的地方。
  • 是的,无论后台模式如何,都会安排作业。如果您终止您的应用,下一个作业仍将在约 15 分钟内按计划运行。
  • 是的,您可以在没有 BootstrapNotifier 的情况下使用作业。监控告诉您只需进入/退出事件。测距每秒为您提供回调,其中包含检测到的信标列表。两者都使用区域定义,告诉您哪些信标将匹配。所有空值匹配所有信标。
猜你喜欢
  • 1970-01-01
  • 2018-12-30
  • 1970-01-01
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 1970-01-01
  • 2018-03-25
  • 1970-01-01
相关资源
最近更新 更多