Android4.3(api18)开始支持蓝牙Ble(Bluetooth Low Energy)开发,到Android5.0(api21)开始修改了部分方法库,详细请看“蓝牙Ble开发(支持API21<Android 5.0 Jelly>及以上)”。
https://note.youdao.com/ynoteshare1/index.html?id=4e9e1de1c3604df0f5b017eea73a19e1&type=note#/
一、注意
1、所需权限
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
2、蓝牙Ble协议默认最大发送20字节(不包含头尾,包含头尾总共22字节);
二、遗留问题
1、蓝牙发送数据可以正常回调方法onCharacteristicWrite,蓝牙读数据无法正常回调方法onCharacteristicRead,但是可以正常回调方法onCharacteristicChanged,可以通过该方法获取蓝牙已读数据。原因未知。
三、代码详情
1、先判断蓝牙是否开启
// 获取蓝牙管理器 BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); // 获取蓝牙适配器 BluetoothAdapter bAdapter = bluetoothManager.getAdapter(); // 判断是否可用 if(bAdapter.isEnabled()) { // 蓝牙已打开
} else { // 弹出蓝牙打开对话框(系统) Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); startActivityForResult(intent, 1); }
蓝牙选择结果:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { // 蓝牙选择结果判断 if (requestCode == 1) { switch (resultCode) { case 120: // 蓝牙已打开 break; case 0: // 蓝牙未打开 break; } } }
2、开始扫描(回调扫描结果方法)-- bAdapter.startLeScan(leScanCallback)返回device
/** * 开始扫描ble */ boolean canReconntect = false; public void startLeScan() { // 开始扫描后允许重连,防止意外断开连接 canReconntect = true; if (bAdapter != null) { boolean result = bAdapter.startLeScan(leScanCallback); if (result) { // LE开始扫描 } Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { // 扫描10s后停止扫描 stopLenScan(); } }, 10000); } } /** * 停止扫描 */ public void stopLenScan() { if (bAdapter != null) { bAdapter.stopLeScan(leScanCallback); LogUtils.instance().logI("LE扫描停止..."); } } // 扫描回调方法 private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { // 扫描结果 device.getName() if (device.getName() != null && device.getName().equals("指定蓝牙名称")) { LogUtils.instance().logI("扫描到指定结果:" + device.getName()); // 停止扫描 stopLenScan(); // 根据扫描到的蓝牙地址进行连接 requestConnect(device); LogUtils.instance().logI("开始获取gatt..."); } } };
3、建立链接 -- device.connectGatt(context, true, bluetoothGattCallback)返回gatt
/** *发起连接(供外部调用) * @param device:目标设备 */ public void requestConnect(BluetoothDevice device) { BluetoothGatt gatt = device.connectGatt(context, true, mGattCallback); // 为了防止重复的gatt,连接成功先检查是否有重复的,有则断开 BluetoothGatt last = listBluetoothGatts.remove(device.getAddress()); if(last != null) { last.disconnect(); last.close(); } // 添加当前gatt listBluetoothGatts.put(device.getAddress(), gatt); } /** * 断开连接 * @param address */ public synchronized void disconnect(String address) { if (null != listBluetoothGatts && listBluetoothGatts.containsKey(address)) { BluetoothGatt gatt = listBluetoothGatts.remove(address); if (gatt != null) { gatt.disconnect(); gatt.close(); LogUtils.instance().logE("LE GATT释放成功," + gatt.getDevice().getName()); } } } /** * 释放所有连接 */ public void disconnectAll() { // 手动停止后不需要重连 canReconntect = false; if(null != listBluetoothGatts) { for(String address : listBluetoothGatts.keySet()) { disconnect(address); } } LogUtils.instance().logI("已释放所有连接"); }
4、发现服务 -- gatt.discoverServices()返回gatt
5、获取服务,并查找出与通信有关的服务 -- gatt.getServices()返回服务列表listBluetoothGattService
6、获取特征值(读、写) -- bluetoothGattService.getCharacteristics()返回bluetoothGattCharacteristic
/** * gatt连接状态回调 */ private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { /** * 连接状态改变回调 * @param gatt Gatt服务对象 * @param status 是否成功执行连接操作 * @param newState 连接状态(已经连接、已经断开) */ @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); String address = gatt.getDevice().getAddress(); // 成功执行连接操作 if(status == BluetoothGatt.GATT_SUCCESS) { // 设备已连接 if(newState == BluetoothProfile.STATE_CONNECTED) { // 重置重连次数 reConnectCount = 0; LogUtils.instance().logI("LE设备已连接"); // 发现服务进行通信(成功会回调方法onServicesDiscovered()) if(gatt != null) { // 发现服务成功后回调onServicesDiscovered()方法 if(gatt.discoverServices()) { LogUtils.instance().logI( "LE发现服务正常"); } else { LogUtils.instance().logE( "LE发现服务失败"); } } } // 设备已断开 else if(newState == BluetoothProfile.STATE_DISCONNECTED) { // 先断开原有连接 disconnect(address); LogUtils.instance().logE( "LE设备已断开"); // 重新连接 reConnectCount ++; if(reConnectCount <= Constant.RE_CONNECT_COUNT && canReconntect) { requestConnect(gatt.getDevice()); LogUtils.instance().logI( "LE设备开始重新连接"); } else { LogUtils.instance().logE( "LE设备重新连接次数已到上线;" + reConnectCount + "或已手动停止"); } } } else { LogUtils.instance().logE( "LE连接Gatt失败" + status + "//" + newState); // 如果断开则重新连接 if(newState != BluetoothProfile.STATE_CONNECTED && canReconntect) { // 先断开原有连接 disconnect(address); reConnectCount ++; if(reConnectCount <= Constant.RE_CONNECT_COUNT && canReconntect) { // 重新连接 requestConnect(gatt.getDevice()); LogUtils.instance().logI( "LE设备开始重新连接"); } else { LogUtils.instance().logE( "LE设备重新连接次数已到上线;" + reConnectCount + "或已手动停止"); } } } } /** * 发现服务回调 * @param gatt * @param status */ @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); LogUtils.instance().logI( "LE发现服务回调"); // api21以上方法,以下不支持 // boolean r = gatt.requestMtu(100); // LogUtils.instance().logE(r + "突破"); if(status == BluetoothGatt.GATT_SUCCESS && gatt != null) { // 获取服务列表(包含通信的服务) listGattServices = gatt.getServices(); for(BluetoothGattService s : listGattServices) { if(s.getType() == BluetoothGattService.SERVICE_TYPE_PRIMARY && s.getUuid().toString().equals(Constant.UUID_SERVER.toLowerCase())) { // 找到指定通信服务(使用UUID匹配) LogUtils.instance().logI( "LE发现蓝牙为主模式,UUID Service确认OK" + listGattServices.size()); // 给Servic、Gatt赋值,供主线程调用 bluetoothGattMain = gatt; List<BluetoothGattCharacteristic> listCharacteristic = s.getCharacteristics(); for(BluetoothGattCharacteristic c : listCharacteristic) { // 写入数据(蓝牙工作模式) if(c.getUuid().toString().equals(Constant.UUID_RECEIVE.toLowerCase())) { // 给Servic、Gatt赋值,供主线程调用 bluetoothGattCharacteristicWriteMain = c; } else if(c.getUuid().toString().equals(Constant.UUID_TRANS.toLowerCase())) { // 给Servic、Gatt赋值,供主线程调用 bluetoothGattCharacteristicReadMain = c; } // 扫描到蓝牙设备 reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("listenerBluetoothState", "蓝牙已连接"); } } else if(s.getType() == BluetoothGattService.SERVICE_TYPE_SECONDARY) { LogUtils.instance().logI( "LE发现蓝牙为从模式"); } } } } /** * 数据接收回调(未回调,待确认...) * @param gatt * @param characteristic * @param status */ @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); LogUtils.instance().logI( "onCharacteristicRead"); if(status == BluetoothGatt.GATT_SUCCESS) { LogUtils.instance().logI( "LE蓝牙数据读入成功回调:"); } else { LogUtils.instance().logE( "LE蓝牙数据读入失败回调:"); } } /** * 数据发送回调(OK) * @param gatt * @param characteristic * @param status */ @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); LogUtils.instance().logI( "LE蓝牙模式发送成功回调:" + characteristic.getValue().toString()); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); // LogUtils.instance().logI( "LE onCharacteristicChanged:" + characteristic.getValue().toString()); getDataFromCharacteristic(characteristic); } /** * * @param gatt * @param descriptor * @param status */ @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } };
7、使用对应的进行读写操作
蓝牙读写操作尽量放在主线程中,所以这里需要发送广播,在广播接收器中进行蓝牙读写操作,
/** * 发送通知 */ public void startNotification() { Intent iWrite = new Intent(); iWrite.setAction(Constant.BLE_WRITE); context.sendBroadcast(iWrite); // 发送广播开始从蓝牙读数据 Intent iRead = new Intent(); iRead.setAction(Constant.BLE_READ); context.sendBroadcast(iRead); }
广播接收器接收消息,进行蓝牙读写操作:
public class BluetoothBroadcast extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(Constant.BLE_WRITE)) { LogUtils.instance().logE("广播接收器收到消息了"); // LogUtils.instance().logE("广播接收器:" + BluetoothBleUtils.instance().bluetoothGatt.getDevice().getName() + "///" + Thread.currentThread().getName()); BluetoothGattCharacteristic characteristicWrite = BluetoothBleLowUtils.instance().bluetoothGattCharacteristicWriteMain; characteristicWrite.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); characteristicWrite.setValue(Constant.BLUETOOTH_MODE); BluetoothGatt gatt = BluetoothBleLowUtils.instance().bluetoothGattMain; gatt.setCharacteristicNotification(characteristicWrite, true); boolean result = gatt.writeCharacteristic(characteristicWrite); LogUtils.instance().logE("广播接收器:开始写了" + result); } if(intent.getAction().equals(Constant.BLE_READ)) { LogUtils.instance().logE("广播接收器:开始读了"); BluetoothGattCharacteristic characteristicRead = BluetoothBleLowUtils.instance().bluetoothGattCharacteristicReadMain; BluetoothGatt gatt = BluetoothBleLowUtils.instance().bluetoothGattMain; gatt.setCharacteristicNotification(characteristicRead, true); for(BluetoothGattDescriptor descriptor : characteristicRead.getDescriptors()) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); boolean b = gatt.writeDescriptor(descriptor); } // boolean result = gatt.readCharacteristic(characteristicRead); LogUtils.instance().logE("广播接收器:开始读了"); } } }