一个基于rxjava2的AndroidBle库-RxAndroidBluetooth
19年4月25日更新前言:一个基于rxjava2的AndroidBLe库github简书目的:保持代码更好的可读性控制代码的复杂度更优雅的处理异步怎么体现?举个栗子?就拿扫描来说,在没有使用rx时它可能是这样的scanner.startScan(filters, settings, scanCallback)callback:override fun onScanRe...
19年4月25日更新
前言:一个基于rxjava2的AndroidBLe库
github
19年4月25日更新
V1.3:第一版
19年5月10日更新
V1.4:修复了在Android4.4上扫描的Api兼容性问题
19年12/04更新
V1.6:增加对多包指令的更好支持
19年12/30日更新
V1.8:有一种场景是,当手机端连接上蓝牙设备后,蓝牙设备立即主动发送了一条信息,这种情况下库也是支持的,但是之前没有特殊说明,在连接成功后的监听中是可以收到的,具体看下面的第五项,(感觉自己写的有点乱。。好像只有我自己能看懂一样。。。。好吧本来估计也就我自己在用)
另外受不了bintray了。每次更新都一堆网络错误,实在废时间,转投jitpack,所以依赖方式变了,不过不影响之前的使用
目的:
- 保持代码更好的可读性
- 控制代码的复杂度
- 更优雅的处理异步
怎么体现?
举个栗子?
就拿扫描来说,在没有使用rx时它可能是这样的
scanner.startScan(filters, settings, scanCallback)
callback:
override fun onScanResult(callbackType: Int, result: android.bluetooth.le.ScanResult?) {
// 解析
}
如果你需要对低版本兼容添加一些过滤规则 它可能是这样的:
override fun onScanResult(callbackType: Int, result: android.bluetooth.le.ScanResult?) {
if(xx == xx){
//...
}
}
如果你需要对扫描进行控时 它可能是这样的:
Handler().postDelayed({
scanner.stop(scanCallback)
},5000)
scanner.startScan(filters, settings, scanCallback)
你甚至需要有其他更多的需求,在子线程开启扫描?等等等等就不一一列举了
但是现在使用RxAndroidBluetooth,它是这样的:
bluetoothController!!
.startLeScan()
.subscribe(
{ checkScanResult(it) },
{ error -> checkError(error) },
{ Log.d(tag, "扫描完成") })
对低版本兼容过滤?一个filter操作符解决
bluetoothController!!
.startLeScan()
.filter { response ->
response.getDevice()?.name == "abc"
}
.subscribe(
{ checkScanResult(it) },
{ error -> checkError(error) },
{ Log.d(tag, "扫描完成") })
对扫描控制时间?一个timer操作符解决
bluetoothController!!
.startLeScan()
.timer(6000, TimeUnit.MILLISECONDS)
.filter { response ->
response.getDevice()?.name == "abc"
}
.subscribe(
{ checkScanResult(it) },
{ error -> checkError(error) },
{ Log.d(tag, "扫描完成") })
开启子线程扫描?
bluetoothController!!
.startLeScan()
.timer(6000, TimeUnit.MILLISECONDS)
.filter { response ->
response.getDevice()?.name == "abc"
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ checkScanResult(it) },
{ error -> checkError(error) },
{ Log.d(tag, "扫描完成") })
其他各种骚操作?rxjava操作符多着呢
bluetoothController!!
.startLeScan()
.timer(6000, TimeUnit.MILLISECONDS)
.filter { response ->
!TextUtils.isEmpty(response.getDevice()?.name)
}
.map {
it.getDevice()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
checkScanResult(it)
},
{ error -> Log.d(tag, "扫描出错$error") },
{ Log.d(tag, "扫描完成") })
我不会rxjava怎么办,能使用吗?那不顺势学习一波?带着需求学习,事半功倍
上面只是简单的介绍了使用RxAndroidBluetooth的一些简便之处,下面介绍详细使用方法
依赖
将jitpack添加到项目的build.gradle中
allprojects {
repositories {
...
maven { url 'https://www.jitpack.io' }
}
}
将库的依赖添加到module的build.gradle中
implementation 'com.github.duoshine:AndroidBluetoothPro:1.8'
初始化
private var bluetoothController: BluetoothWorker? = null
bluetoothController = BluetoothController
.Builder(this)
.setNotifyUuid(notifyUUID)
.setServiceUuid(serviceUUID)
.setWriteUuid(writeUuid)
.build()
tips:所有可操作Api都在BluetoothWorker中.下面挨个介绍,使用不分先后顺序
1.startLeScan
开启扫描,如果你的设备>=6.0请自行处理好定位权限,代码:
scanDispose = bluetoothController!!
.startLeScan()
.timer(6000, TimeUnit.MILLISECONDS)
.subscribe(
{ checkScanResult(it) },
{ error -> checkError(error) },
{ Log.d(tag, "扫描完成") })
startLeScan支持添加过滤条件,但是只支持API21及以上的设备扫描使用(调用者不需要维护版本差异,低于API21的设备RxAndroidBluetooth不会使用过滤条件,即使你传递了过滤规则)
timer(非rxjava原生的静态操作符)定时扫描,如果不使用则一直扫描,直到dispose调用
扫描结果:ScanResult,这个对象需要介绍一下,它所支持的参数随着设备的等级提升而提升,对于API21一下的设备,稳定输出的只有(BluetoothDevice&rssi&scanRecord),使用时请注意
停止扫描
scanDispose?.dispose()
ps:每次扫描任务之前都需要.dispose(),否则你将开启两个扫描任务,这点很容易理解和rxjava+retrofit使用是一样的,你开启了两个request
2.writeOnce
写操作-适用于单包指令,代码:
bluetoothController!!
.writeOnce(byteArray)
.subscribe(
{ response -> checkResult(response) },
{ error -> checkError(error) }
)
使用它你只需要处理Response就可以了,Response的处理极为简单,只需要一个通用的解析函数即可:
private fun checkResult(response: Response) {
when (response.code) {
BluetoothWriteProfile.writeSucceed -> Log.d(tag, "写入成功")
BluetoothWriteProfile.writeFail -> Log.d(tag, "写入失败")
BluetoothWriteProfile.characteristicChanged -> Log.d(tag, "收到新值-${Arrays.toString(response.data)}")
}
}
3.writeAuto
写操作-适用于多包指令,它的表现形式是自动发送,接收一个list集合
bluetoothController!!
.writeAuto(list)
.subscribe(
{ response -> checkResult(response) },
{ error -> checkError(error) }
)
我通常不建议使用此函数来执行写操作,它的执行原理是写入成功即发送下一包,它的结果处理:
private fun checkResult(response: Response) {
when (response.code) {
BluetoothWriteProfile.writeSucceed -> Log.d(tag, "写入成功")
BluetoothWriteProfile.writeFail -> Log.d(tag, "写入失败")
BluetoothWriteProfile.characteristicChanged -> Log.d(tag, "收到新值-${Arrays.toString(response.data)}")
}
}
4.writeNext
写操作-适用于多包指令,它的表现形式是调用者决定是否发送下一包,接收一个list集合
bluetoothController!!
.writeNext(list)
.doOnNext(Function { byte ->
BluetoothNextProfile.next
})
.subscribe(
{ response -> checkResult(response) },
{ error -> checkError(error) }
)
使用此函数你只需要实现doOnNext(非rxjava原生,而是RxAndroidBluetooth的),它接收一个Function<ByteArray,Int>,输入类型是当前包返回的结果,调用者也许需要对此远程设备返回的数据进行效验?解密?或其他操作来决定是否继续发送下一包,请查看BluetoothNextProfile中的功能码,它支持重发等其他操作
5.connect
连接远程设备
connectDisposable = bluetoothController!!
.connect("xx:xx:xx:xx:xx:xx")
.auto()
.timer(6000, TimeUnit.MILLISECONDS)
.subscribe(
{ response -> checkResultState(response) },
{ error -> checkError(error) }
)
connect支持断开自动连接(非手动,如调用dispose后则不会重连),你只需要一个auto即可支持断开自动重连
connect支持连接超时限制,你只需要一个timer操作符即可实现
扫描结果处理(更多功能码请参考BluetoothConnectProfile):
private fun checkResultState(response: Response) {
when (response.code) {
BluetoothConnectProfile.connected -> Log.d(tag, "连接成功")
BluetoothConnectProfile.disconnected -> Log.d(tag, "断开连接")
BluetoothConnectProfile.connectTimeout -> Log.d(tag, "连接超时")
BluetoothConnectProfile.enableNotifySucceed -> Log.d(tag, "启用通知特征成功")
BluetoothConnectProfile.enableNotifyFail -> Log.d(tag, "启用通知特征失败")
BluetoothConnectProfile.serviceNotfound -> Log.d(tag, "未获取到对应uuid的服务特征")
BluetoothConnectProfile.notifyNotFound -> Log.d(tag, "未获取到对应uuid的通知特征")
BluetoothConnectProfile.reconnection -> Log.d(tag, "重连中")
}
}
断开连接:
connectDisposable?.dispose()
note:如果手机端连接ble设备后,ble设备主动上报了一条信息,那么这里是可以接收到的,只要在上面的 when表达式中多添加一个case即可,功能码是BluetoothWriteProfile.characteristicChanged,演示代码如下
/**
* ble状态监听
*/
private fun checkResultState(response: Response) {
var msg = ""
when (response.code) {
BluetoothConnectProfile.connected -> msg = "连接成功"
BluetoothConnectProfile.disconnected -> msg = "断开连接"
BluetoothConnectProfile.connectTimeout -> msg = "连接超时"
BluetoothConnectProfile.enableNotifySucceed -> msg = "启用通知特征成功"
BluetoothConnectProfile.enableNotifyFail -> msg = "启用通知特征失败"
BluetoothConnectProfile.serviceNotfound -> msg = "未获取到对应uuid的服务特征"
BluetoothConnectProfile.notifyNotFound -> msg = "未获取到对应uuid的通知特征"
BluetoothConnectProfile.reconnection -> msg = "重连中"
//当code为18时 是收到设备主动上传的数据
else -> checkResult(response)
}
Log.d(tag, msg)
}
这里我没有具体使用BluetoothWriteProfile.characteristicChanged,而是使用了else,但是为了严谨你们还是认真一点写。。。
ps:每次连接任务之前最好都需要.dispose(),防止上次连接没有完成,否则你将开启两个连接任务
6.isEnabled
蓝牙是否启用
bluetoothController!!.isEnabled()
7.device
获取gatt对应的远程设备(不处于连接中也可以调用) 这个设备可能是当前正在连接的设备或是上一次连接的设备
bluetoothController!!
.device()
.subscribe(
{device-> Log.d(tag, "$device")},
{error-> Log.d(tag, "$error")},
{ Log.d(tag, "完成")})
8.enable
开启蓝牙
bluetoothController!!.enable()
note
-
你可能需要在不需要扫描及断开连接的地方合适地调用dispose,这和平时使用rxjava是一样的,避免内存泄漏
-
你如果不在subscribe中处理onError,那么它将由Android捕获,RxAndroidBluetooth维护的异常都在这个包内
duoshine.rxandroidbluetooth.exception
-
RxAndroidBluetooth的队列不会维护所有指令,它仅仅只维护单条指令(多包的单条指令),如果在并发环境下,它永远都将只处理后一条指令,在并发的环境下它并不能很好的处理多条指令(多包的单条指令),所以调用者如果是并发处理多包指令,请做好测试!!!,单包指令则没有问题(比如开启线程执行心跳包指令)
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)