RxAndroidBle是针对Android蓝牙低功耗头痛的强大止痛药。它得到RxJava的支持,将复杂的API作为方便的可观察对象来实现。该库为您做:
要获得支持,请前往StackOverflow #rxandroidble
阅读Polidea Blog上的官方公告。
维护客户端的单个实例是您的工作。您可以使用单例作用域Dagger组件或任何其他所需的组件。
RxBleClient rxBleClient = RxBleClient.create(context);
该库不处理管理BluetoothAdapter的状态。
不建议直接管理状态,因为它侵犯了应用程序用户管理手机状态的权利。见Javadoc
的BluetoothAdapter.enable()方法。
用户有责任告知应用程序为何需要打开蓝牙并征得应用程序用户同意的责任。
通过调用以下命令,可以显示打开蓝牙的本机活动:
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
int REQUEST_ENABLE_BT = 1;
context.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
扫描该区域中的设备非常简单:
Disposable scanSubscription = rxBleClient.scanBleDevices(
new ScanSettings.Builder()
// .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // change if needed
// .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
.build()
// add filters if needed
)
.subscribe(
scanResult -> {
// Process scan result here.
},
throwable -> {
// Handle an error here.
}
);
// When done, just dispose.
scanSubscription.dispose();
对于API <21(在Lollipop之前)的设备,将模拟扫描API以获取相同的行为。
在Android上,确定特定的BLE操作是否具有成功的潜力并不总是一件容易的事。例如以扫描在Android 6.0设备需要具有一个BluetoothAdapter
时,应用程序需要具有授予运行时允许针对任一ACCESS_COARSE_LOCATION
或ACCESS_FINE_LOCATION
,额外地Location Services
需要时被导通。为确保扫描仅在一切准备就绪后才能使用,您可以使用:
Disposable flowDisposable = rxBleClient.observeStateChanges()
.switchMap(state -> { // switchMap makes sure that if the state will change the rxBleClient.scanBleDevices() will dispose and thus end the scan
switch (state) {
case READY:
// everything should work
return rxBleClient.scanBleDevices();
case BLUETOOTH_NOT_AVAILABLE:
// basically no functionality will work here
case LOCATION_PERMISSION_NOT_GRANTED:
// scanning and connecting will not work
case BLUETOOTH_NOT_ENABLED:
// scanning and connecting will not work
case LOCATION_SERVICES_NOT_ENABLED:
// scanning will not work
default:
return Observable.empty();
}
})
.subscribe(
rxBleScanResult -> {
// Process scan result here.
},
throwable -> {
// Handle an error here.
}
);
// When done, just dispose.
flowDisposable.dispose();
对于进一步的BLE交互,需要连接。
String macAddress = "AA:BB:CC:DD:EE:FF";
RxBleDevice device = rxBleClient.getBleDevice(macAddress);
Disposable disposable = device.establishConnection(false) // <-- autoConnect flag
.subscribe(
rxBleConnection -> {
// All GATT operations are done through the rxBleConnection.
},
throwable -> {
// Handle an error here.
}
);
// When done... dispose and forget about connection teardown :)
disposable.dispose();
自动连接
从BluetoothDevice.connectGatt()Javadoc:
autoConnect布尔值:是直接连接到远程设备(false)还是在远程设备可用后立即自动连接(true)。
乍一看,自动连接概念可能会引起误解。将autoconnect标志设置为false时,如果在RxBleDevice#establishConnection
调用该方法时BLE设备未发布广告,则连接将最终出错。从平台到平台的超时不同,发出错误的时间有所不同,但通常是几十秒而不是几秒钟(约30 s)。
将自动连接标志设置为true允许您等待直到发现BLE设备为止。在RxBleConnection
完全建立连接之前,不会发出该实例。根据经验,它还可以处理唤醒锁的获取,因此可以安全地假定在建立连接后即可唤醒您的Android设备-但这不是文档功能,在将来的系统版本中可能会有所变化。与本机Android API不同,如果autoConnect=true
在使用此库时不会丢失原始连接,则不会尝试自动重新连接。
注意不要过度使用autoConnect标志。另一方面,它对连接初始化速度有负面影响。扫描窗口和间隔降低了,因为它已针对后台使用进行了优化,并且取决于蓝牙参数,它可能(并且通常确实)花费更多时间来建立连接。
读
device.establishConnection(false)
.flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
.subscribe(
characteristicValue -> {
// Read characteristic value.
},
throwable -> {
// Handle an error here.
}
);
写
device.establishConnection(false)
.flatMapSingle(rxBleConnection -> rxBleConnection.writeCharacteristic(characteristicUUID, bytesToWrite))
.subscribe(
characteristicValue -> {
// Characteristic value confirmed.
},
throwable -> {
// Handle an error here.
}
);
多次读取
device.establishConnection(false)
.flatMap(rxBleConnection -> Single.zip(
rxBleConnection.readCharacteristic(firstUUID),
rxBleConnection.readCharacteristic(secondUUID),
YourModelCombiningTwoValues::new
))
.subscribe(
model -> {
// Process your model.
},
throwable -> {
// Handle an error here.
}
);
长写
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.createNewLongWriteBuilder()
.setCharacteristicUuid(uuid) // required or the .setCharacteristic()
// .setCharacteristic() alternative if you have a specific BluetoothGattCharacteristic
.setBytes(byteArray)
// .setWriteOperationRetryStrategy(retryStrategy) // if you'd like to retry batch write operations on failure, provide your own retry strategy
// .setMaxBatchSize(maxBatchSize) // optional -> default 20 or current MTU
// .setWriteOperationAckStrategy(ackStrategy) // optional to postpone writing next batch
.build()
)
.subscribe(
byteArray -> {
// Written data.
},
throwable -> {
// Handle an error here.
}
);
读写结合
device.establishConnection(false)
.flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid)
.doOnSuccess(bytes -> {
// Process read data.
})
.flatMap(bytes -> rxBleConnection.writeCharacteristic(characteristicUuid, bytesToWrite))
)
.subscribe(
writeBytes -> {
// Written data.
},
throwable -> {
// Handle an error here.
}
);
device.establishConnection(false)
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(characteristicUuid))
.doOnNext(notificationObservable -> {
// Notification has been set up
})
.flatMap(notificationObservable -> notificationObservable) // <-- Notification has been set up, now observe value changes.
.subscribe(
bytes -> {
// Given characteristic has been changes, here is the value.
},
throwable -> {
// Handle an error here.
}
);
如果您想观察设备连接状态的变化,只需按如下所示进行订阅。订阅后,您将立即收到最新状态。
device.observeConnectionStateChanges()
.subscribe(
connectionState -> {
// Process your way.
},
throwable -> {
// Handle an error here.
}
);
对于连接调试,您可以使用扩展日志记录
RxBleClient.setLogLevel(RxBleLog.DEBUG);
默认情况下,RxBleLog
使用logcat打印消息。您可以提供自己的记录器实现,以将其转发到其他记录库,例如Timber。
RxBleLog.setLogger((level, tag, msg) -> Timber.tag(tag).log(level, msg));
您可能遇到的每个错误都是通过onError
回调提供的。每个公共方法都有JavaDoc解释可能的错误。
从不同的接口,您可以获得Observable
具有不同行为的s。Observable
您可能会遇到两种类型的。
RxBleClient.scan()
,RxBleDevice.observeConnectionStateChanges()
和Observable
由发射RxBleConnection.setupNotification()
/RxBleConnection.setupIndication()
setupNotification()
/ setupIndication()
-在您处置时,通知/指示将被禁用RxBleDevice.establishConnection()
是一个Observable
会发出单个信号RxBleConnection
但不会完成的信号,因为以后连接可能会出错(即外部断开连接)。每当您不再对保持连接打开感兴趣时,都应进行处理,这将导致断开连接和清理资源。
下表概述了使用的Observable
模式
接口 | 功能 | 值数 | 热冷 |
---|---|---|---|
RxBleClient | scanBleDevices()* | 无穷 | 冷 |
RxBleClient | watchStateChanges() | 无穷** | 热 |
RxBleDevice | watchConnectionStateChanges() | 无穷 | 热 |
RxBleDevice | establishConnection()* | 一 | 冷 |
RxBleConnection | setupNotification()* | 一 | 冷 |
RxBleConnection | 发出setupNotification()的Observable | 无穷** | 热 |
RxBleConnection | setupIndication()* | 一 | 冷 |
RxBleConnection | 发出setupIndication()的Observable | 无穷** | 热 |
RxBleConnection | queue() | 用户自定义 | 冷 |
*这Observable
在处置时会关闭/清理内部资源(即完成扫描,关闭连接,禁用通知)
**这Observable
可能会完成。例如observeStateChanges()
,当设备上没有蓝牙适配器时,它仅发出一个值并在一种情况下完成。没有理由监视其他状态,因为适配器在运行时不会出现。第二个示例:从setupNotification
/发出的ObservablesetupIndication
可以在处置父Observable时完成。
我们鼓励您检查包装com.polidea.rxandroidble2.helpers
,com.polidea.rxandroidble2.utils
其中包含一些典型使用情况的方便的反应式包装。
价值解读
蓝牙规范指定格式,其中int
/ float
/String
值可以被存储在特性。BluetoothGattCharacteristic
具有用于检索这些功能的功能(.getIntValue()
/ .getFloatValue()
/ .getStringValue()
)。由于RxAndroidBle
读取和通知发出,因此byte[]
您可能希望使用ValueIntepreter
帮助程序轻松检索相同的数据。
观察BluetoothAdapter状态
如果您想观察BluetoothAdapter
状态变化,可以使用RxBleAdapterStateObservable
。
自API 23(6.0 / Marshmallow)起的Android需要在清单中声明位置权限,以便应用程序运行BLE扫描。RxAndroidBle已经在AndroidManifest中为您提供了所有必需的蓝牙权限。
运行BLE扫描所需的运行时权限:
来自API | 到API(含) | 可接受的运行时权限 |
---|---|---|
18岁 | 22 | (无需运行时权限) |
23 | 28 | 其中一个下面: - android.permission.ACCESS_COARSE_LOCATION - android.permission.ACCESS_FINE_LOCATION |
29 | 当前 | -- android.permission.ACCESS_FINE_LOCATION |
潜在的许可问题
Google AndroidManifest
在发布到Play商店时正在检查是否声明权限。如果您有标签ACCESS_COARSE_LOCATION
或ACCESS_FINE_LOCATION
使用标签进行了手动设置uses-permission
(与相对uses-permission-sdk-23
),则可能会遇到清单未与RxAndroidBle合并的问题,从而导致无法上传到Play商店。仅在SDK 23+上才需要这些权限。如果您在较低版本的Android上需要这些权限中的任何一个,请将您的声明替换为:
使用示例位于:
/sample
/sample-kotlin
请记住,这些只是显示如何使用库的示例。这些并不是要成为好的应用程序体系结构的榜样。
implementation "com.polidea.rxandroidble2:rxandroidble:1.11.1"
com.polidea.rxandroidble2
rxandroidble
1.11.1
aar
如果您对尖端构建感兴趣,则可以获取SNAPSHOT
该库的版本。注意:快照是从master
和develop
分支的顶部构建的,并且要进行更频繁的更改,这些更改可能会破坏API和/或更改行为。
要下载它,您需要将Sonatype Snapshot存储库站点添加到build.gradle
文件中:
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
库使用的大多数对象都是可以模拟的接口的实现。
另一种选择是使用MockRxAndroidBle
(下面有更多信息)。注意:MockRxAndroidBle
在单元测试中使用需要Robolectric。
有时需要在不访问物理设备的情况下开发应用程序。我们已经创建了MockRxAndroidBle作为模拟简单外围设备的插件。
不幸的是,它还没有得到积极的发展,尽管PR受到欢迎。;)
如果您在应用程序中遇到的与此库有关的看似错误的行为,请检查以下常见问题列表: