Luch简介— Android上的BLE信标扫描新库

Luch是一个轻量级的Android库,用于简单易用的BLE信标扫描,支持不同类型的信标格式,具有良好的测试覆盖率。文章介绍了Luch与现有解决方案的比较、安装、设置扫描、过滤信标数据以及距离计算等功能。
摘要由CSDN通过智能技术生成

Bluetooth Beacons are the small devices that transmit small packages of data according to the BLE protocol. These signals can be picked up by nearby devices like smartphones, and these devices can act accordingly.

蓝牙信标是根据BLE协议传输小数据包的小型设备。 这些信号可以被附近的设备(例如智能手机)接收,并且这些设备可以相应地起作用。

For the last couple of months, I’ve been trying out BLE APIs on Android. I wanted to build a beacon scanner library that would fit my needs perfectly while being as simple as possible. The general curiosity about how things work in the land of BLE on Android was also one of the main drivers.

F或在过去的几个月中,我一直在尝试在Android BLE的API。 我想建立一个信标扫描程序库,该库可以尽可能完美地满足我的需求。 对Android上BLE领域的工作原理的普遍好奇心也是主要驱动力之一。

(You see, I’m doing my best to avoid calling it a severe case of Not-Invented-Here syndrome).

(您知道,我正在尽力避免将其称为“ Not-Invented-Here综合征” 的严重病例 )。

The result of these explorations is the new library called Luch* which has the following features:

这些探索的结果是一个名为Luch *的新库,它具有以下功能:

  • Easy to use API;

    易于使用的API;
  • Small footprint — the entire AAR is a little bit over 50 Kb;

    占地面积小-整个AAR略高于50 Kb;
  • Good test coverage (at the time of this writing it stands at 94% according to the Codecov reports);

    良好的测试覆盖率(在撰写本文时,根据Codecov报告,该覆盖率达到94%);
  • Support of different beacon types (AltBeacon beacons are supported out of the box, other beacons can be supported by providing the beacon layout);

    支持不同的信标类型(开箱即用地支持AltBeacon信标,可以通过提供信标布局来支持其他信标);
  • Distance calculation for detected beacons with RSSI filters to smooth out some shakiness of RSSI data.

    使用RSSI过滤器对检测到的信标进行距离计算,以消除RSSI数据的某些抖动。

But first, let’s take a look at the existing solutions.

但是首先,让我们看一下现有的解决方案。

比较方式 (Comparison)

To make matters simpler, I decided to put the data on the solutions I’ve been looking at while working on Luch into a single table:

为简化起见,我决定将在处理Luch时一直在寻找的解决方案中的数据放在一个表中:

I’d say that the choice boils down to this:

我会说选择归结为:

  1. If you need to detect beacons when the app is backgrounded or killed, choose between AltBeacon and iBeacon scanner android (but you might have to change your plans regarding background detections soon, more on that later**).

    如果您需要在应用程序被后台或杀死时检测信标,请在AltBeaconiBeacon扫描仪android之间进行选择( 但您可能需要尽快更改有关背景检测的计划 ,稍后再进行**)。

  2. If you need to detect non-iBeacon beacons when the app is backgrounded or killed, choose AltBeacon.

    如果在应用程序被后台或杀死时需要检测非iBeacon信标,请选择AltBeacon。
  3. Another thing to consider is whether the library is still supported or not. The last commit to iBeacon scanner android was in April 2018, so this may or may not affect your decision. AltBeacon is still supported, and I feel like the maintainer has no reason to stop supporting it. :)

    要考虑的另一件事是是否仍支持该库。 对iBeacon扫描仪android的最后一次提交是在2018年4月,因此这可能会或可能不会影响您的决定。 AltBeacon仍受支持,我觉得维护人员没有理由停止支持它。 :)
  4. Otherwise, I’d recommend giving Luch a try.

    否则,建议您尝试使用Luch。

Now, let’s look at some APIs (I’ll consider AltBeacon beacons for the most of the article).

现在,让我们看一些API(本文中大部分内容将考虑使用AltBeacon信标)。

安装 (Installation)

The library is added to jCenter, so you only need to add a dependency to your app’s build.gradle file:

该库已添加到jCenter,因此您只需向应用程序的build.gradle文件添加依赖build.gradle

implementation 'aga.android:luch:(insert latest version)'

设置扫描 (Setting up the scans)

If all you want*** is to be notified about all beacons that surround you periodically, you need to create a BeaconScanner first:

如果想要将所有围绕您的信标定期通知所有***,则需要首先创建一个BeaconScanner

val beaconScanner = BeaconScanner.Builder(this)
    .setBeaconBatchListener { beacons: Collection<Beacon> ->
        // do something with your beacons here
    }
    .build()

Later, you will need to start the scans when the app is in the foreground and stop them when it gets backgrounded. I usually write my apps according to the Single Activity style, so these starts and stops naturally correspond to the onResume and onPause callbacks:

稍后,您将需要在应用程序处于前台时启动扫描,并在其进入​​后台时停止扫描。 我通常根据Single Activity样式编写应用,因此这些启动和停止自然对应于onResumeonPause回调:

override fun onResume() {
    super.onResume()
    beaconScanner.start()
}


override fun onPause() {
    beaconScanner.stop()
    super.onPause()
}

You’ll need to check that the app holds location permission before starting and stopping the scans, but I omitted that for brevity.

在开始和停止扫描之前,您需要检查该应用程序是否具有位置权限,但是为了简洁起见,我将其省略。

Also, this in my example points to the Context which is needed to access BLE APIs on Android.

另外,在我的示例中, this指向访问Android上的BLE API所需的Context

Don’t forget to check that Bluetooth and Location Services are turned on, and the necessary permissions are given; the library won’t crash or show any popups if they aren’t.

不要忘记检查蓝牙和定位服务是否已打开,并且已获得必要的权限。 库不会崩溃,也不会显示任何弹出窗口。

And that’s it!

就是这样!

通过信标数据过滤 (Filtering by the beacon data)

Now, let’s consider something else. What if you want to make sure the library only looks for some specific AltBeacon beacons? To do that, you set up your BeaconScanner with someRegions :

现在,让我们考虑其他问题。 如果要确保库仅查找某些特定的AltBeacon信标怎么办? 为此,您可以使用一些Regions设置BeaconScanner

val region = Region.Builder()
    .setNullField()
    .setUuidField(UUID.fromString("01234567-0123-4567-89AB-456789ABCDEF"))
    .setIntegerField(154)
    .setIntegerField(10122)
    .build()


val beaconScanner = BeaconScanner.Builder(this)
  .setRegions(listOf(region))
  .setBeaconBatchListener { beacons: Collection<Beacon> ->
      // do something with your beacons here
  }
  .build()

Each AltBeacon beacon is identified by three fields, named id1 (16-byte long field), id2 and id3 (both are 2-byte long integer fields). The setUuidField method in the code above sets up id1, the first setIntegerField invocation sets up id2; the second invocation sets id3.

每个AltBeacon信标由三个字段标识,分别称为id1 (16字节长的字段), id2id3 (均为2字节长的整数字段)。 上面代码中的setUuidField方法设置了id1 ,第一个setIntegerField调用设置了id2 ; 第二个调用设置id3

Then you pass your regions into the scanner. You can provide multiple regions when you’re setting up the scanner; I used a single one to simplify an example.

然后,将您的区域传递到扫描仪中。 设置扫描仪时,可以提供多个区域。 我只用一个来简化示例。

Voilà, we’re done.****

Voilà,我们完成了。****

信标格式 (Beacon formats)

If you want to look for the different beacons, you can do that by specifying the custom beacon layout format:

如果要查找其他信标,则可以通过指定自定义信标布局格式来实现:

val beaconLayout = "<beacon-layout>" // search the Internet to find the layout string of your specific beacon


val beaconParser = BeaconParserFactory.createFromLayout(beaconLayout)


val beaconScanner = BeaconScanner.Builder(this)
    .setBeaconParser(beaconParser)
    .setBeaconBatchListener { beacons: Collection<Beacon> ->
        // do something with your beacons here
    }
    .build()

The format of beacon layouts is somewhat similar to the one supported by AltBeacon library(see setBeaconLayout method) with the number of exceptions:

信标布局的格式与AltBeacon库(请参阅setBeaconLayout方法)支持的格式有些类似,但有以下例外:

  1. The only field prefixes supported at the moment are ‘m’, ‘i’, ‘p’ and ’d’.

    目前唯一支持的字段前缀是“ m”,“ i”,“ p”和“ d”。
  2. Little-endian fields are not supported yet, as variable-length fields.

    尚不支持使用Little-endian字段作为可变长度字段。

距离计算 (Distance calculation)

You can range your beacons if you want to. To do that, build your BeaconScanner with ranging support:

您可以根据需要调整信标范围。 为此,请使用范围支持来构建您的BeaconScanner

val beaconScanner = BeaconScanner.Builder(this)
    .setBeaconBatchListener { beacons: Collection<Beacon> ->
        // do something with your beacons here
    }
    .setRangingEnabled()
    .build()

Once you start beacon scans, you can access the scanner’s Ranger object. This object does the distance calculation for a detected beacon:

一旦开始信标扫描,就可以访问扫描仪的Ranger对象。 该对象对检测到的信标进行距离计算:

val ranger = beaconScanner.getRanger()
val distance = ranger.calculateDistance(beacon)

The ranging works only for the beacons that provide the TxPower value in their advertisement packages (AltBeacon is one of them).

测距仅适用于在其广告包中提供TxPower值的信标(AltBeacon是其中之一)。

Another component of distance calculation is RSSI value, which changes over time. Due to the nature of BLE, RSSI values can change quite suddenly, even if you’re not moving and just standing in front of the beacon.

距离计算的另一个组成部分是RSSI值,它随时间变化。 由于BLE的性质,即使您不移动而只是站在信标的前面,RSSI值也会突然改变。

To smooth these sudden changes, Luch uses the RSSI filtering technique. The default filter is running average filter, but you can replace it with ARMA (autoregressive–moving-average filter):

为了平滑这些突然的变化,Luch使用RSSI过滤技术。 默认过滤器为运行平均过滤器,但您可以将其替换为ARMA (自回归移动平均过滤器):

val beaconScanner = BeaconScanner.Builder(this)
    .setBeaconBatchListener { beacons: Collection<Beacon> ->
        // do something with your beacons here
    }
    .setRangingEnabled(
        ArmaFilter.Builder()
    )
    .build()
beaconScanner.start()

You can provide your own filters by extending the RssiFilter class.

您可以通过扩展RssiFilter类来提供自己的过滤器。

This is more or less all I wanted to mention about this library, there’re some additional examples in the repository’s readme and sample module. I’ve been using it myself for close to a month now, and all the use-cases I’ve been interested in work flawlessly.

这或多或少是我想提及的关于该库的全部内容,该存储库的自述文件和示例模块中还有一些其他示例。 我自己已经使用了近一个月,而且我一直对工作感兴趣的所有用例都完美无缺。

I recommend anyone interested in beacons on Android to give this library a try and share your feedback!

我建议任何对Android上的信标感兴趣的人都可以尝试该库并分享您的反馈!

笔记 (Notes)

* Luch (луч) means Beam (or Ray) in Russian.

* Luch(луч)在俄语中是Beam(或Ray)的意思。

** The reason you might want to reconsider the idea of background beacon scans is that these scans require the location permission. And, as you might know, Google is cracking down on the background location access yet again.

**您可能要重新考虑后台信标扫描的想法的原因是,这些扫描需要位置许可。 而且,您可能知道,Google再次严厉打击了后台位置访问

In a nutshell, you have to either provide a piece of really compelling evidence that your use-case justifies background location access, or you need to remove it from your app. The failure to comply will result in the app’s removal from Google Play Store.

简而言之,您必须提供一个非常有说服力的证据来证明您的用例证明了后台位置访问的合理性,或者您需要将其从应用程序中删除。 不遵守将导致该应用程序从Google Play商店中删除

** If you want to listen to individual beacon’s enter/exit/update events, you can do that too:

**如果您想听单个信标的输入/退出/更新事件,也可以这样做:

val beaconScanner = BeaconScanner.Builder(this)
    .setBeaconListener(
        object : IBeaconListener {
            override fun onBeaconEntered(beacon: Beacon) {
                // do something with your beacon
            }


            override fun onBeaconExited(beacon: Beacon) {
                // do something with your beacon
            }


            override fun onBeaconUpdated(beacon: Beacon) {
                // do something with your beacon
            }
        }
    )
    .build()

**** If you’re curious about why you need to add a null field into your Region first, the reason is quite simple. An AltbBeacon’s beacon layout looks like that:

****如果您对为什么需要首先在您的地区中添加空字段感到好奇,那么原因很简单。 AltbBeacon的信标布局如下所示:

m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25

m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25

The first field is a beacon type field that occupies the 2nd and 3rd bytes and has a value of "beac". Then we have an id1 identifier field (bytes 4-19), id2 identifier field (bytes 20-21), id3 identifier field (bytes 22-23), and two additional single-byte fields. Since we don’t want to filter by the beacon type, we ignore it by specifying the null value for that field.

第一个字段是信标类型字段,它占据第二个和第三个字节,其值为"beac" 。 然后,我们有一个id1标识符字段(字节4-19), id2标识符字段(字节20-21), id3标识符字段(字节22-23)和两个附加的单字节字段。 由于我们不想按信标类型进行过滤,因此可以通过为该字段指定null值来忽略它。

You might be tempted to ask — yeah, that’s a good explanation, but why do we even need that null field? Can’t we omit it somehow?

您可能会想问-是的,这是一个很好的解释,但是为什么我们甚至需要该null字段? 我们不能以某种方式忽略它吗?

We certainly can, but it’ll only make the implementation of the BeaconParser a little bit more challenging to grasp. ‘Explicit is better than implicit’, as they say in “The Zen of Python”. :)

我们当然可以,但是这只会使BeaconParser的实现更具挑战性。 正如他们在“ The Zen of Python ”中所说的那样,“显式胜于隐式”。 :)

翻译自: https://proandroiddev.com/introducing-luch-a-new-library-for-ble-beacon-scans-on-android-50c1cb1623c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值