自己简单封装了一个可以实现多连接的蓝牙BLE的库,主要对蓝牙扫描、连接、接受广播通知、写数据进行了封装,一个功能不是很复杂的BLEManager实现蓝牙BLE的多连接。
完整项目放在github上
扫描
提供了一个可以设置超时的扫描方法,主要是考虑到蓝牙扫描比较耗电,扫描使用了职责更为清晰的BluetoothLeScanner。
/**
* 扫描
* @param view
*/
public void scan(View view) {
bleManager.startScan(10000, new BLEScanner.OnBLEScanListener() {
@Override
public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
synchronized (MainActivity.this){
if(!macList.contains(device.getAddress())){
macList.add(device.getAddress());
adapter.notifyDataSetChanged();
}
}
}
@Override
public void onScanFailed(BLEException bleException) {
toast(bleException.getMessage());
}
});
}
连接
连接其实可以分为两步,首先是与指定的设备进行连接,然后是找服务。
为了实现多连接创建了一个BluetoothGatt的缓存池,缓存池里主要存放每个mac地址对应的设备的BluetoothGatt和BluetoothGattCallback回调方法,设备连接成功或断开连接、找到服务、接受广播通知、写数据成功、获取到设备返回数据等后面要用到的几个事件都是在BluetoothGattCallback中获取到事件的回调的。
/**
* 连接设备
*
* @param mac
* @param onBLEConnectListener
*/
public void connect(final String mac, final OnBLEConnectListener onBLEConnectListener) {
BLEGattCallback bleGattCallback = bleBluetoothGattPool.getBluetoothGattCallback(mac);
if (bleGattCallback == null) {
bleGattCallback = new BLEGattCallback();
}
bleGattCallback.setOnBLEConnectListener(new OnBLEConnectListener() {
@Override
public void onConnectSuccess(BluetoothGatt gatt, int status, int newState) {
onBLEConnectListener.onConnectSuccess(gatt, status, newState);
}
@Override
public void onConnectFailure(BluetoothGatt gatt, BLEException bleException) {
if (gatt != null) {
disconnectGatt(mac);
}
if (gatt != null && gatt.getDevice().getAddress().toUpperCase().equals(mac.toUpperCase())) {
BLELogUtil.e(TAG, "onConnectFailure");
onBLEConnectListener.onConnectFailure(gatt, bleException);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
onBLEConnectListener.onServicesDiscovered(gatt, status);
}
});
BluetoothGatt bluetoothGatt = bleBluetoothGattPool.getBluetoothGatt(mac);
if (bluetoothGatt == null) {
//缓存池中不存在改gatt
bluetoothGatt = new BLEConnect().connect(application, mac, bleGattCallback);
}
if (bluetoothGatt == null) {
onBLEConnectListener.onConnectFailure(null, new BLEException(BLEException.CONNECT_FAILURE));
}
//添加到缓存池中
bleBluetoothGattPool.setBluetoothGatt(mac, bluetoothGatt, bleGattCallback);
}
写特征值
有时可能需要接受广播通知,需要写入特定特征值,写入成功有回调。
使用时传入服务的uuid,特征值的uuid和要数据返回的特征值的uuid。
bleManager.writeDescriptor(currentMac, uuidDescriptorService, uuidDescriptorCharacteristic, uuidDescriptor, new OnBLEWriteDescriptorListener() {
@Override
public void onWriteDescriptorSuccess(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
toast("接受通知成功");
}
@Override
public void onWriteDescriptorFailure(BLEException bleException) {
toast(bleException.getMessage());
}
});
写数据
写数据主要是封装了一个分包写数据,之前说过蓝牙BLE发数据每包是有限制的MTU是23,进行分包每次写入20个数据,同时两包数据之间进行一定的时间间隔,虽然蓝牙传输是可靠的数据传输方式,理论上是不会出现丢包的现象的,但是太过频繁的发送数据还是出现了丢包现象。
/**
* 写一组数据
*
* @param gatt
* @param gattCallback
* @param bluetoothGattCharacteristic
* @param data
* @param position
*/
private void writeOneSet(final BluetoothGatt gatt, final BLEGattCallback gattCallback, final BluetoothGattCharacteristic bluetoothGattCharacteristic, final byte[] data, int position) {
currentPosition = position;
if (position == 0) {
//第一组数据
gattCallback.setOnBLEWriteDataListener(new OnBLEWriteDataListener() {
@Override
public void onWriteDataSuccess(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if ((currentPosition + 1) * MAX_BYTES >= data.length) {
//数据完全写完
onBLEWriteDataListener.onWriteDataSuccess(gatt, characteristic, status);
return;
}
writeOneSet(gatt, gattCallback, bluetoothGattCharacteristic, data, currentPosition + 1);
}
@Override
public void onWriteDataFailure(BLEException exception) {
onBLEWriteDataListener.onWriteDataFailure(exception);
}
});
}
new Thread(new Runnable() {
@Override
public void run() {
try {
//两包数据间间隔一定时间
Thread.sleep(WAIT_TIME);
int sendLength = data.length - currentPosition * MAX_BYTES;
sendLength = sendLength > MAX_BYTES ? MAX_BYTES : sendLength;
byte[] sendValue = BLEByteUtil.getSubbytes(data, currentPosition * MAX_BYTES, sendLength);
BLELogUtil.i(TAG, String.format("position=%d,%s", currentPosition, BLEByteUtil.bytesToHexString(sendValue)));
if (!bluetoothGattCharacteristic.setValue(sendValue)) {
BLELogUtil.e(TAG, "writeOneSet setValue failure");
onBLEWriteDataListener.onWriteDataFailure(new BLEException(BLEException.WRITE_DATA_FAILURE));
return;
}
if (!gatt.writeCharacteristic(bluetoothGattCharacteristic)) {
BLELogUtil.e(TAG, "writeOneSet writeCharacteristic failure");
onBLEWriteDataListener.onWriteDataFailure(new BLEException(BLEException.WRITE_DATA_FAILURE));
return;
}
} catch (Exception e) {
BLELogUtil.e(TAG, "writeOneSet e:" + e.getMessage());
}
}
}).start();
}