.setScanTimeOut(10000) // 扫描超时时间,可选,默认10秒
.build();
BleManager.getInstance().initScanRule(scanRuleConfig);
以上准备工作完成后,就可以开始进行扫描。
BleManager.getInstance().scan(new BleScanCallback() {
@Override
public void onScanStarted(boolean success) {
}
@Override
public void onLeScan(BleDevice bleDevice) {
}
@Override
public void onScanning(BleDevice bleDevice) {
}
@Override
public void onScanFinished(List scanResultList) {
}
});
onScanStarted(boolean success)
: 会回到主线程,参数表示本次扫描动作是否开启成功。由于蓝牙没有打开,上一次扫描没有结束等原因,会造成扫描开启失败。
onLeScan(BleDevice bleDevice)
:扫描过程中所有被扫描到的结果回调。由于扫描及过滤的过程是在工作线程中的,此方法也处于工作线程中。同一个设备会在不同的时间,携带自身不同的状态(比如信号强度等),出现在这个回调方法中,出现次数取决于周围的设备量及外围设备的广播间隔。
onScanning(BleDevice bleDevice)
:扫描过程中的所有过滤后的结果回调。与onLeScan区别之处在于:它会回到主线程;同一个设备只会出现一次;出现的设备是经过扫描过滤规则过滤后的设备。
onScanFinished(List<BleDevice> scanResultList)
:本次扫描时段内所有被扫描且过滤后的设备集合。它会回到主线程,相当于onScanning设备之和。
1.4. 设备信息
扫描得到的BLE外围设备,会以BleDevice
对象的形式,作为后续操作的最小单元对象。它本身含有这些信息:
String getName()
:蓝牙广播名
String getMac()
:蓝牙Mac地址
byte[] getScanRecord()
: 被扫描到时候携带的广播数据
int getRssi()
:被扫描到时候的信号强度
后续进行设备连接、断开、判断设备状态,读写操作等时候,都会用到这个对象。可以把它理解为外围蓝牙设备的载体,所有对外围蓝牙设备的操作,都通过这个对象来传导。
1.5. 连接、断连、监控连接状态
拿到设备对象之后,可以进行连接操作。
BleManager.getInstance().connect(bleDevice, new BleGattCallback() {
@Override
public void onStartConnect() {
}
@Override
public void onConnectFail(BleException exception) {
}
@Override
public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
}
@Override
public void onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status) {
}
});
onStartConnect()
:开始进行连接。
onConnectFail(BleException exception)
:连接不成功。
onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status)
:连接成功并发现服务。
onDisConnected(boolean isActiveDisConnected, BleDevice bleDevice, BluetoothGatt gatt, int status)
:连接断开,特指连接后再断开的情况。在这里可以监控设备的连接状态,一旦连接断开,可以根据自身情况考虑对BleDevice对象进行重连操作。需要注意的是,断开和重连之间最好间隔一段时间,否则可能会出现长时间连接不上的情况。此外,如果通过调用disconnect(BleDevice bleDevice)
方法,主动断开蓝牙连接的结果也会在这个方法中回调,此时isActiveDisConnected将会是true。
1.6. GATT协议
BLE连接都是建立在 GATT (Generic Attribute Profile) 协议之上。GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。它定义两个 BLE 设备通过Service 和 Characteristic 进行通信。GATT 就是使用了 ATT(Attribute Protocol)协议,ATT 协议把 Service, Characteristic以及对应的数据保存在一个查找表中,次查找表使用 16 bit ID 作为每一项的索引。
关于GATT这部分内容会在下面重点讲解。总之,中心设备和外设需要双向通信的话,唯一的方式就是建立 GATT 连接。当连接成功之后,外围设备与中心设备之间就建立起了GATT连接。
上面讲到的connect(BleDevice bleDevice, BleGattCallback bleGattCallback)
方法其实是有返回值的,这个返回值就是BluetoothGatt
。当然还有其他方式可以获取BluetoothGatt
对象,连接成功后,调用:
BluetoothGatt gatt = BleManager.getInstance().getBluetoothGatt(BleDevice bleDevice);
通过BluetoothGatt
对象作为连接桥梁,中心设备可以获取外围设备的很多信息,以及双向通信。
首先,就可以获取这个蓝牙设备所拥有的Service和Characteristic。每一个属性都可以被定义作不同的用途,通过它们来进行协议通信。下面的方法,就是通过BluetoothGatt
,查找出所有的Service和Characteristic的UUID:
List serviceList = bluetoothGatt.getServices();
for (BluetoothGattService service : serviceList) {
UUID uuid_service = service.getUuid();
List characteristicList= service.getCharacteristics();
for(BluetoothGattCharacteristic characteristic : characteristicList) {
UUID uuid_chara = characteristic.getUuid();
}
}
1.7. 协议通信
APP与设备建立了连接,并且知道了Service和Characteristic(需要与硬件协议沟通确认)之后,我们就可以通过BLE协议进行通信了。通信的桥梁,主要就是是通过 标准的或者自定义的Characteristic,中文我们称之为“特征”。我们可以从 Characteristic 读数据和写数据。这样就实现了双向的通信。站在APP作为中心设备的角度,常用于数据交互的通信方式主要有3种:接收通知、写、读,此外还有设置最大传输单元,获取实时信号强度等通信操作。
- 接收通知
有两种方式可以接收通知,indicate和notify。indicate和notify的区别就在于,indicate是一定会收到数据,notify有可能会丢失数据。indicate底层封装了应答机制,如果没有收到中央设备的回应,会再次发送直至成功;而notify不会有central收到数据的回应,可能无法保证数据到达的准确性,优势是速度快。通常情况下,当外围设备需要不断地发送数据给APP的时候,比如血压计在测量过程中的压力变化,胎心仪在监护过程中的实时数据传输,这种频繁的情况下,优先考虑notify形式。当只需要发送很少且很重要的一条数据给APP的时候,优先考虑indicate形式。当然,从Android开发角度的出发,如果硬件放已经考虑了成熟的协议和发送方式,我们需要做的仅仅是根据其配置的数据发送方式进行相应的对接即可。
//打开notify
BleManager.getInstance().notify(
bleDevice,
uuid_service,
uuid_characteristic_notify,
new BleNotifyCallback() {
@Override
public void onNotifySuccess() {
// 打开通知操作成功
}
@Override
public void onNotifyFailure(BleException exception) {
// 打开通知操作失败
}
@Override
public void onCharacteristicChanged(byte[] data) {
// 打开通知后,设备发过来的数据将在这里出现
}
});
//关闭notify
BleManager.getInstance().stopNotify(uuid_service, uuid_characteristic_notify);