android蓝牙BLE使用小结
最近产品新增一个小功能,具体需求是:
手机APP搜索连接蓝牙设备,向蓝牙设备写指令,蓝牙设备写指令回调,通知APP。
虽然功能简单,但实现时候也遇到一些小坑,整理下希望后来人少走弯路。
基础概念:
-
版本要求
最低支持BLE的Android 系统是 4.3 Jelly Bean,也就是 API 18 才开始支持低功耗蓝牙,但只能作为中心设备去连接其他设备。
从 Android 5.0 开始才支持外设模式。 -
传统蓝牙和低功耗蓝牙的区别
BLE,全称为Bluetooth Low Energy,即蓝牙低功耗,较传统蓝牙最大的特点就是低功耗,多应用于对实时性要求较高,
但对数据传输速率要求比较低的场景,比如血压计、键鼠等设备,而语音、音乐等对数据量传输比较大的场景依然需要使用传统蓝牙。
参考
蓝牙BLE介绍
Android蓝牙开发—经典蓝牙和BLE(低功耗)蓝牙的区别
- 几个关键的类:
- BluetoothManager: 类似WindowManager的系统服务类。我们的应用主要是通过该类获取BluetoothAdapter对象。
- BluetoothAdapter: 可以通过该类开启关闭蓝牙,扫描蓝牙设备。
- BluetoothGatt: 蓝牙设备连接后获取到的对象,通过该类可以实现主从设备间通信操作
- BluetoothAdapter.LeScanCallback: BluetoothAdapter扫描蓝牙设备后的回调接口
- BluetoothGattCallback BluetoothGatt的回调接口,主从设备通信接口回调。
- BluetoothGattService: 蓝牙GATT服务,一个蓝牙设备对应多个蓝牙GATT服务。
-
BluetoothGattCharacteristic: 蓝牙GATT 特征,一个蓝牙服务对应多个蓝牙GATT特征。
参考:Android BLE 蓝牙开发
- 涉及到的权限:
Manifest.permission.BLUETOOTH //允许应用去连接蓝牙设备
Manifest.permission.BLUETOOTH_ADMIN //允许应用找到与之连接的蓝牙设备
Manifest.permission.ACCESS_COARSE_LOCATION //android 6.0 后使用蓝牙需要该权限
遇到的问题:
1.
搜索回调收不到
因为开启蓝牙搜索以及连接回调都是新起了一个线程,导致经常回调收不到,
自己测试了下,
A线程启动搜索连接,回调正常,结束操作。
B线程启动搜索连接,回调到A线程里面去。
后来stackflow上查了下,建议这些操作最好都放在主线程内。
相关问题,子线程开的定时器,延时发送,关闭的时候都会错乱。
2.
传输失败
因为BLE协议,每次写BluetoothGattCharacteristic只能20个字节。
所以要做分包传输,两次传输最好间隔500ms
3.
同一设备,多次连接后会提示连接失败。
操作处理完毕后,BluetoothGatt及时断开,最好先BluetoothGatt.disconnect();
然后在LeScanCallback里BluetoothGatt.close();
因为同一个主设备最多可以保证六个左右的连接。若多次连接没有及时断开,可能会引起这个问题。
4.
设备连接过慢
/**
* autoConnect 设置为false
* transport设置为BluetoothDevice.TRANSPORT_LE
**/
BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport)
autoConnect 文档这么描述的:
Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true).
BluetoothDevice.TRANSPORT_LE 文档这么描述的:
Prefer LE transport for GATT connections to remote dual-mode devices
/**
* status 参数表示 GATT通信的结果 GATT_SUCCESS 为成功
* newState 连接状态,是BluetoothProfile.STATE_DISCONNECTED,BluetoothProfile.STATE_CONNECTED二者选一
**/
BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport)
其中 BluetoothGattCallback中
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState)
5.
BluetoothGattCallback中 onConnectionStateChange回调两个参数简述
/**
* status GATT通信的结果 GATT_SUCCESS 为成功
* newState 连接状态,BluetoothProfile.STATE_DISCONNECTED,BluetoothProfile.STATE_CONNECTED二者选一
**/
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState)
6.
找不到BluetoothGattService的一个解决方案
通过反射清理之前连接的缓存。(讲是9.0屏蔽反射的渠道,但用三星S9+,和华为P20都正常使用)
private boolean refreshDeviceCache(BluetoothGatt gatt) {
try {
BluetoothGatt localBluetoothGatt = gatt;
Method localMethod = localBluetoothGatt.getClass().getMethod("refresh", new Class[0]);
if (localMethod != null) {
boolean bool = ((Boolean) localMethod.invoke(localBluetoothGatt, new Object[0])).booleanValue();
return bool;
}
} catch (Exception localException) {
}
return false;
}