【Android】蓝牙开发——BLE(低功耗蓝牙)(附完整Demo)
目录
目录
前言
一、相关概念介绍
二、实战开发
三、项目演示
四、Demo案例源码地址
五、更新记录
1、2020/12/29 :修改 setupService()中错误
2、2021/05/14 :更新连接方法(解决部分蓝牙设备连接失败的问题)
3、2022/11/25 :实际开发过程,需要将三个UUID改成具体蓝牙设备的UUID
问:如何查看具体蓝牙设备中的这三个UUID?
答:手机下载nrf工具app,搜索连接上蓝牙设备后,即可查看蓝牙设备中支持的服务UUID、读写特征UUID等信息。
前言
之前的几篇文章,主要介绍了经典蓝牙开发相关的知识,今天我们就来看看低功耗蓝牙的开发。如果小伙伴们对之前的文章感兴趣兴趣,也可以看看,欢迎提出不足或者建议。
【Android】蓝牙开发——经典蓝牙(附Demo源码)
【Android】蓝牙开发——经典蓝牙配对介绍(通过手机系统蓝牙演示)
【Android】蓝牙开发—— 经典蓝牙配对介绍(Java代码实现演示)附Demo源码
一、相关概念介绍
BLE,全称 Bluetooth Low Energy,即低功耗蓝牙。BLE关键术语和概念,可以查看官网介绍:
蓝牙低功耗概览 | Android 开发者 | Android Developers
首先,我们需要知道,BLE设备有以下几个方面的东西:
1、一个BLE设备包含多个服务,这些服务通过UUID来区分。
2、一个服务包含1个或多个特征,这些特征也是通过UUID来区分。
3、一个特征包含一个Value和多个描述符,一个描述符包含一个Value,
其次,我们来看一看Android中BLE相关的对象:
1、BluetoothDevice :蓝牙设备
2、BluetoothGatt:建立的连接
3、BluetoothGattCallback:连接回调
3、BluetoothGattService:服务
4、BluetoothGattCharacteristic:服务的特征
5、BluetoothGattDescriptor:特征的描述
了解了这些之后,我们开始进入BLE开发阶段。
二、实战开发
注意:BLE是在Android 4.3(API 18)以后引入的,所以要进行BLE开发,必须在Android 4.3以上版本的机子上。
1、添加相关权限
(1)蓝牙权限
<!-- 应用使用蓝牙的权限 --> <uses-permission android:name="android.permission.BLUETOOTH"/> <!--启动设备发现或操作蓝牙设置的权限--> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
(2)位置权限(BLE与经典蓝牙相比,还需要位置权限,如果没有,有的机型是扫描不到设备的,注意Android6.0以后还需要动态申请位置权限)
<!--位置权限--> <!--Android 10以上系统,需要ACCESS_FINE_LOCATION--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!--Android 9以及以下系统,需要ACCESS_FINE_LOCATION--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
2、使用蓝牙之前,首先要检查当前手机是否支持BLE蓝牙。如果支持BLE蓝牙,检查手机蓝牙是否已开启。如果没有开启,则需要先打开蓝牙。打开手机蓝牙,有两种方式,一种是直接enable()打开,另外一种是提示用户打开,推荐第二种方式。
/*** 检测手机是否支持4.0蓝牙* @param context 上下文* @return true--支持4.0 false--不支持4.0*/private boolean checkBle(Context context){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { //API 18 Android 4.3bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);if(bluetoothManager == null){return false;}bluetooth4Adapter = bluetoothManager.getAdapter(); //BLUETOOTH权限if(bluetooth4Adapter == null){return false;}else{Log.d(TAG,"该设备支持蓝牙4.0");return true;}}else{return false;}}
/*** 获取蓝牙状态*/public boolean isEnable(){if(bluetooth4Adapter == null){return false;}return bluetooth4Adapter.isEnabled();}/*** 打开蓝牙* @param isFast true 直接打开蓝牙 false 提示用户打开*/public void openBluetooth(Context context,boolean isFast){if(!isEnable()){if(isFast){Log.d(TAG,"直接打开手机蓝牙");bluetooth4Adapter.enable(); //BLUETOOTH_ADMIN权限}else{Log.d(TAG,"提示用户去打开手机蓝牙");Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);context.startActivity(enableBtIntent);}}else{Log.d(TAG,"手机蓝牙状态已开");}}
3、系统蓝牙已打开,则可以开启扫描设备。需要注意的是,扫描设备是耗时的操作,一旦扫描结束,就要及时停止扫描。
扫描设备 /////扫描设备回调@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {@Overridepublic void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] bytes) {//在onLeScan()回调中尽量做少的操作,可以将扫描到的设备扔到另一个线程中处理if(bluetoothDevice == null)return;if(bluetoothDevice.getName() != null){Log.d(TAG,bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress());}else{Log.d(TAG,"null" + "-->" + bluetoothDevice.getAddress());}BLEDevice bleDevice = new BLEDevice(bluetoothDevice,rssi);if(onDeviceSearchListener != null){onDeviceSearchListener.onDeviceFound(bleDevice); //扫描到设备回调}}};/*** 设置时间段 扫描设备* @param onDeviceSearchListener 设备扫描监听* @param scanTime 扫描时间*/public void startDiscoveryDevice(OnDeviceSearchListener onDeviceSearchListener,long scanTime){if(bluetooth4Adapter == null){Log.e(TAG,"startDiscoveryDevice-->bluetooth4Adapter == null");return;}this.onDeviceSearchListener = onDeviceSearchListener;if(onDeviceSearchListener != null){onDeviceSearchListener.onDiscoveryStart(); //开始扫描回调}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {Log.d(TAG,"开始扫描设备");bluetooth4Adapter.startLeScan(leScanCallback);}else{return;}//设定最长扫描时间mHandler.postDelayed(stopScanRunnable,scanTime);}private Runnable stopScanRunnable = new Runnable() {@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)@Overridepublic void run() {if(onDeviceSearchListener != null){onDeviceSearchListener.onDiscoveryOutTime(); //扫描超时回调}//scanTime之后还没有扫描到设备,就停止扫描。stopDiscoveryDevice();}};
4、扫描到目标设备之后,开始建立连接,这里注意,连接之前一定要关闭扫描,否则会影响连接。BLE与经典蓝牙不同,经典蓝牙一旦建立连接,就可以进行数据通讯,而BLE建立连接之后,还需要发现系统服务,获取特定服务及读写特征。
(1)建立连接 & 发现系统服务
/ 执行连接 ////连接/通讯结果回调@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {@Overridepublic void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyUpdate(gatt, txPhy, rxPhy, status);}@Overridepublic void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {super.onPhyRead(gatt, txPhy, rxPhy, status);}//连接状态回调-连接成功/断开连接@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);Log.d(TAG,"status:" + status);Log.d(TAG,"newState:" + newState);switch(status){case BluetoothGatt.GATT_SUCCESS:Log.w(TAG,"BluetoothGatt.GATT_SUCCESS");break;case BluetoothGatt.GATT_FAILURE:Log.w(TAG,"BluetoothGatt.GATT_FAILURE");break;case BluetoothGatt.GATT_CONNECTION_CONGESTED:Log.w(TAG,"BluetoothGatt.GATT_CONNECTION_CONGESTED");break;case BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION:Log.w(TAG,"BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION");break;case BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION:Log.w(TAG,"BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION");break;case BluetoothGatt.GATT_INVALID_OFFSET:Log.w(TAG,"BluetoothGatt.GATT_INVALID_OFFSET");break;case BluetoothGatt.GATT_READ_NOT_PERMITTED:Log.w(TAG,"BluetoothGatt.GATT_READ_NOT_PERMITTED");break;case BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED:Log.w(TAG,"BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED");break;}BluetoothDevice bluetoothDevice = gatt.getDevice();Log.d(TAG,"连接的设备:" + bluetoothDevice.getName() + " " + bluetoothDevice.getAddress());isConnectIng = false;//移除连接超时mHandler.removeCallbacks(connectOutTimeRunnable);if(newState == BluetoothGatt.STATE_CONNECTED){Log.w(TAG,"连接成功");//连接成功去发现服务gatt.discoverServices();//设置发现服务超时时间mHandler.postDelayed(serviceDiscoverOutTimeRunnable,MAX_CONNECT_TIME);if(onBleConnectListener != null){onBleConnectListener.onConnectSuccess(gatt,bluetoothDevice,status); //连接成功回调}}else if(newState == BluetoothGatt.STATE_DISCONNECTED) {//清空系统缓存ClsUtils.refreshDeviceCache(gatt);Log.e(TAG, "断开连接status:" + status);gatt.close(); //断开连接释放连接if(status == 133){//无法连接if(onBleConnectListener != null){onBleConnectListener.onConnectFailure(gatt,bluetoothDevice,"连接异常!",status); //133连接异常 异常断开Log.e(TAG,"连接失败status:" + status + " " + bluetoothDevice.getAddress());}}else if(status == 62){//成功连接没有发现服务断开if(onBleConnectListener != null){onBleConnectListener.onConnectFailure(gatt,bluetoothDevice,"连接成功服务未发现断开!",status); //62没有发现服务 异常断开Log.e(TAG,"连接成功服务未发现断开status:" + status);}}else if(status == 0){if(onBleConnectListener != null){onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //0正常断开 回调}}else if(status == 8){//因为距离远或者电池无法供电断开连接// 已经成功发现服务if(onBleConnectListener != null){onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //8断电断开 回调}}else if(status == 34){if(onBleConnectListener != null){onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //34断开}}else {//其它断开连接if(onBleConnectListener != null){onBleConnectListener.onDisConnectSuccess(gatt,bluetoothDevice,status); //其它断开}}}else if(newState == BluetoothGatt.STATE_CONNECTING){Log.d(TAG,"正在连接...");if(onBleConnectListener != null){onBleConnectListener.onConnecting(gatt,bluetoothDevice); //正在连接回调}}else if(newState == BluetoothGatt.STATE_DISCONNECTING){Log.d(TAG,"正在断开...");if(onBleConnectListener != null){onBleConnectListener.onDisConnecting(gatt,bluetoothDevice); //正在断开回调}}}//发现服务@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);//移除发现服务超时mHandler.removeCallbacks(serviceDiscoverOutTimeRunnable);Log.d(TAG,"移除发现服务超时");Log.d(TAG,"发现服务");//获取特定服务及特征if(setupService(gatt,serviceUUID,readUUID,writeUUID)){if(onBleConnectListener != null){onBleConnectListener.onServiceDiscoverySucceed(gatt,gatt.getDevice(),status); //成功发现服务回调}}else{if(onBleConnectListener != null){onBleConnectListener.onServiceDiscoveryFailed(gatt,gatt.getDevice(),"获取服务特征异常"); //发现服务失败回调}}}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);Log.d(TAG,"读status: " + status);}//向蓝牙设备写入数据结果回调@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);if(characteristic.getValue() == null){Log.e(TAG,"characteristic.getValue() == null");return;}//将收到的字节数组转换成十六进制字符串String msg = TypeConversion.bytes2HexString(characteristic.getValue(),characteristic.getValue().length);if(status == BluetoothGatt.GATT_SUCCESS){//写入成功Log.w(TAG,"写入成功:" + msg);if(onBleConnectListener != null){onBleConnectListener.onWriteSuccess(gatt,gatt.getDevice(),characteristic.getValue()); //写入成功回调}}else if(status == BluetoothGatt.GATT_FAILURE){//写入失败Log.e(TAG,"写入失败:" + msg);if(onBleConnectListener != null){onBleConnectListener.onWriteFailure(gatt,gatt.getDevice(),characteristic.getValue(),"写入失败"); //写入失败回调}}else if(status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED){//没有权限Log.e(TAG,"没有权限!");}}//读取蓝牙设备发出来的数据回调@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);//接收数据byte[] bytes = characteristic.getValue();Log.w("TAG","收到数据str:" + TypeConversion.bytes2HexString(bytes,bytes.length));if(onBleConnectListener != null){onBleConnectListener.onReceiveMessage(gatt,gatt.getDevice(),characteristic,characteristic.getValue()); //接收数据回调}}@Overridepublic void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorRead(gatt, descriptor, status);}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {super.onDescriptorWrite(gatt, descriptor, status);}@Overridepublic void onReliableWriteCompleted(BluetoothGatt gatt, int status) {super.onReliableWriteCompleted(gatt, status);Log.d(TAG,"onReliableWriteCompleted");}@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {super.onReadRemoteRssi(gatt, rssi, status);if(status == BluetoothGatt.GATT_SUCCESS){Log.w(TAG,"读取RSSI值成功,RSSI值:" + rssi + ",status" + status);if(onBleConnectListener != null){onBleConnectListener.onReadRssi(gatt,rssi,status); //成功读取连接的信号强度回调}}else if(status == BluetoothGatt.GATT_FAILURE){Log.w(TAG,"读取RSSI值失败,status:" + status);}}//修改MTU值结果回调@Overridepublic void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {super.onMtuChanged(gatt, mtu, status);///设置mtu值,即bluetoothGatt.requestMtu()时触发,提示该操作是否成功if(status == BluetoothGatt.GATT_SUCCESS){ //设置MTU成功 //MTU默认取的是23,当收到 onMtuChanged 后,会根据传递的值修改MTU,注意由于传输用掉3字节,因此传递的值需要减3。//mtu - 3Log.w(TAG,"设置MTU成功,新的MTU值:" + (mtu-3) + ",status" + status);if(onBleConnectListener != null){onBleConnectListener.onMTUSetSuccess("设置后新的MTU值 = " + (mtu-3) + " status = " + status,mtu - 3); //MTU设置成功}}else if(status == BluetoothGatt.GATT_FAILURE){ //设置MTU失败 Log.e(TAG,"设置MTU值失败:" + (mtu-3) + ",status" + status);if(onBleConnectListener != null){onBleConnectListener.onMTUSetFailure("设置MTU值失败:" + (mtu-3) + " status:" + status); //MTU设置失败}}}};/*** 通过蓝牙设备连接* @param context 上下文* @param bluetoothDevice 蓝牙设备* @param outTime 连接超时时间* @param onBleConnectListener 蓝牙连接监听者* @return*/@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)public BluetoothGatt connectBleDevice(Context context, BluetoothDevice bluetoothDevice, long outTime,OnBleConnectListener onBleConnectListener){if(bluetoothDevice == null){Log.e(TAG,"addBLEConnectDevice-->bluetoothDevice == null");return null;}this.onBleConnectListener = onBleConnectListener;this.curConnDevice = bluetoothDevice;Log.d(TAG,"开始准备连接:" + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress());//出现 BluetoothGatt.android.os.DeadObjectException 蓝牙没有打开try{if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){mBluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback,BluetoothDevice.TRANSPORT_LE,BluetoothDevice.PHY_LE_1M_MASK);}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {mBluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback,BluetoothDevice.TRANSPORT_LE);} else {mBluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback);}}catch(Exception e){Log.e(TAG,"e:" + e.getMessage());}mHandler.postDelayed(connectOutTimeRunnable,outTime);return mBluetoothGatt;}//连接超时private Runnable connectOutTimeRunnable = new Runnable() {@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)@Overridepublic void run() {if(mBluetoothGatt == null){Log.e(TAG,"connectOuttimeRunnable-->mBluetoothGatt == null");return;}isConnectIng = false;mBluetoothGatt.disconnect();//连接超时当作连接失败回调if(onBleConnectListener != null){onBleConnectListener.onConnectFailure(mBluetoothGatt,curConnDevice,"连接超时!",-1); //连接失败回调}}};//发现服务超时private Runnable serviceDiscoverOutTimeRunnable = new Runnable() {@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)@Overridepublic void run() {if(mBluetoothGatt == null){Log.e(TAG,"connectOuttimeRunnable-->mBluetoothGatt == null");return;}isConnectIng = false;mBluetoothGatt.disconnect();//发现服务超时当作连接失败回调if(onBleConnectListener != null){onBleConnectListener.onConnectFailure(mBluetoothGatt,curConnDevice,"发现服务超时!",-1); //连接失败回调}}};
(2)发现系统服务之后,还需要获取特定服务及读写特征才能进行数据通讯。一般,读特征是用来读取蓝牙设备发出来的数据,写特征是向蓝牙设备写入数据,其中,读特征一定要设置打开通知,否则接收不到消息。
/*** 获取特定服务及特征* 1个serviceUUID -- 1个readUUID -- 1个writeUUID* @param bluetoothGatt* @param serviceUUID* @param readUUID* @param writeUUID* @return*/@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)private boolean setupService(BluetoothGatt bluetoothGatt,String serviceUUID,String readUUID,String writeUUID) {if (bluetoothGatt == null) {Log.e(TAG, "setupService()-->bluetoothGatt == null");return false;}if(serviceUUID == null){Log.e(TAG, "setupService()-->serviceUUID == null");return false;}for (BluetoothGattService service : bluetoothGatt.getServices()) { // Log.d(TAG, "service = " + service.getUuid().toString());if (service.getUuid().toString().equals(serviceUUID)) {bluetoothGattService = service;}}//通过上面方法获取bluetoothGattService // bluetoothGattService = bleManager.getBluetoothGattService(bluetoothGatt,ConsData.MY_BLUETOOTH4_UUID);if (bluetoothGattService == null) {Log.e(TAG, "setupService()-->bluetoothGattService == null");return false;}Log.d(TAG, "setupService()-->bluetoothGattService = " + bluetoothGattService.toString());if(readUUID == null || writeUUID == null){Log.e(TAG, "setupService()-->readUUID == null || writeUUID == null");return false;}for (BluetoothGattCharacteristic characteristic : bluetoothGattService.getCharacteristics()) {if (characteristic.getUuid().toString().equals(readUUID)) { //读特征readCharacteristic = characteristic;} else if (characteristic.getUuid().toString().equals(writeUUID)) { //写特征writeCharacteristic = characteristic;}}if (readCharacteristic == null) {Log.e(TAG, "setupService()-->readCharacteristic == null");return false;}if (writeCharacteristic == null) {Log.e(TAG, "setupService()-->writeCharacteristic == null");return false;}//打开读通知enableNotification(true, bluetoothGatt, readCharacteristic);//重点中重点,需要重新设置List<BluetoothGattDescriptor> descriptors = readCharacteristic.getDescriptors();for (BluetoothGattDescriptor descriptor : descriptors) {descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);bluetoothGatt.writeDescriptor(descriptor);}//延迟2s,保证所有通知都能及时打开mHandler.postDelayed(new Runnable() {@Overridepublic void run() {}}, 2000);return true;}/ 打开通知 ///*** 设置读特征接收通知* @param enable 为true打开通知* @param gatt 连接* @param characteristic 特征*/@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)public void enableNotification(boolean enable, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic){if(gatt == null){Log.e(TAG,"enableNotification-->gatt == null");return;}if(characteristic == null){Log.e(TAG,"enableNotification-->characteristic == null");return;}//这一步必须要有,否则接收不到通知gatt.setCharacteristicNotification(characteristic,enable);}
5、数据通讯
(1)发送数据
mBluetoothGatt.writeCharacteristic()方法的返回值,并不能真正的表示数据是否发送成功,而是通过BluetoothGattCallback回调方法onCharacteristicWrite()来判断数据是否已成功写入底层。
//向蓝牙设备写入数据结果回调@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);if(characteristic.getValue() == null){Log.e(TAG,"characteristic.getValue() == null");return;}//将收到的字节数组转换成十六进制字符串String msg = TypeConversion.bytes2HexString(characteristic.getValue(),characteristic.getValue().length);if(status == BluetoothGatt.GATT_SUCCESS){//写入成功Log.w(TAG,"写入成功:" + msg);if(onBleConnectListener != null){onBleConnectListener.onWriteSuccess(gatt,gatt.getDevice(),characteristic.getValue()); //写入成功回调}}else if(status == BluetoothGatt.GATT_FAILURE){//写入失败Log.e(TAG,"写入失败:" + msg);if(onBleConnectListener != null){onBleConnectListener.onWriteFailure(gatt,gatt.getDevice(),characteristic.getValue(),"写入失败"); //写入失败回调}}else if(status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED){//没有权限Log.e(TAG,"没有权限!");}}
/// 发送数据 ////*** 发送消息 byte[]数组* @param msg 消息* @return true false*/@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)public boolean sendMessage( byte[] msg){if(writeCharacteristic == null){Log.e(TAG,"sendMessage(byte[])-->writeGattCharacteristic == null");return false;}if(mBluetoothGatt == null){Log.e(TAG,"sendMessage(byte[])-->mBluetoothGatt == null");return false;}boolean b = writeCharacteristic.setValue(msg);Log.d(TAG, "写特征设置值结果:" + b);return mBluetoothGatt.writeCharacteristic(writeCharacteristic);}
(2)接收数据
接收的数据是直接通过BluetoothGattCallback回调方法onCharacteristicChanged()来获取的。
//读取蓝牙设备发出来的数据回调@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);//接收数据byte[] bytes = characteristic.getValue();Log.w("TAG","收到数据str:" + TypeConversion.bytes2HexString(bytes,bytes.length));if(onBleConnectListener != null){onBleConnectListener.onReceiveMessage(gatt,gatt.getDevice(),characteristic,characteristic.getValue()); //接收数据回调}}
6、断开连接
BLE通讯结束之后,需要及时断开连接,并且在断开连接的回调处释放资源。否则会导致下一次执行连接操作时,导致133异常。所以,一般连接出现133异常,都是因为断开后及时释放资源。
断开连接的结果是在BluetoothGattCallback回调方法onConnectionStateChange()来获取的。(可查看上面建立连接处的代码)
/// 断开连接 ////*** 断开连接*/@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)public void disConnectDevice(){if(mBluetoothGatt == null){Log.e(TAG,"disConnectDevice-->bluetoothGatt == null");return;}//系统断开mBluetoothGatt.disconnect();}
三、项目演示
(1)扫描目标设备(BLEyqy),点击“连接”按钮, 会在“搜索”按钮下方显示连接结果 。
(2)手机给目标设备发送数据成功之后,蓝牙设备把接收到的数据再回发送给手机。
(3)断开连接。点击“断开”按钮, 会在“搜索”按钮下方显示断开结果 。
四、Demo案例源码地址
码云:Android Bluetooth Low Energy: Android 低功耗蓝牙开发
CSDN:AndroidBLE.rar-Android代码类资源-CSDN下载
五、更新记录
1、2020/12/29 :修改 setupService()中错误
2、2021/05/14 :更新连接方法(解决部分蓝牙设备连接失败的问题)
3、2022/11/25 :实际开发过程,需要将三个UUID改成具体蓝牙设备的UUID
问:如何查看具体蓝牙设备中的这三个UUID?
答:手机下载nrf工具app,搜索连接上蓝牙设备后,即可查看蓝牙设备中支持的服务UUID、读写特征UUID等信息。
【Android】蓝牙开发——BLE(低功耗蓝牙)(附完整Demo)相关推荐
- Android 8.0 BLE 低功耗蓝牙开发记录
Android 8.0 BLE 低功耗蓝牙开发记录(1-3)--------------(权限申请篇未完待续) 目的:开源博客,希望大家一起修改博客错误地方,共同完善并会鸣谢提供意见的朋友.为大家提供 ...
- Android 蓝牙开发(三) -- 低功耗蓝牙开发
Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 前面已经学习了经典蓝牙开发,学习了蓝牙的配对连接和通信,又通 ...
- c# 低功耗蓝牙_Android ble低功耗蓝牙开发-客户端
什么是BLE(低功耗蓝牙) BLE(Bluetooth Low Energy,低功耗蓝牙)是对传统蓝牙BR/EDR技术的补充. 尽管BLE和传统蓝牙都称之为蓝牙标准,且共享射频,但是,BLE是一个完全 ...
- BT传统蓝牙和BLE低功耗蓝牙的区别
蓝牙3.0及以下为传统蓝牙. 蓝牙4.0以上标准包含两个蓝牙标准,是一个双模的标准,它包含经典蓝牙部分(Classic Bluetooth)和低功耗蓝牙部分(Bluetooth Low Energ ...
- Qt低功耗蓝牙系列一(什么是低功耗蓝牙开发,低功耗蓝牙的通信机制原理)
文章目录 前言 Android 蓝牙 BLE 低功耗蓝牙协议栈简介 蓝牙的选用 BLE 低功耗蓝牙模块具体应用场景 蓝牙灯控方案 BLE 蓝牙智能锁方案 蓝牙 MAC 地址扫描打印解决方案 蓝牙 Me ...
- windows 蓝牙程序开发 ble低功耗蓝牙
1.需要使用到的动态库 Bluetooth Microsoft.Windows.SDK.Contracts 2.需要使用到的命名控件 using System.Threading.Tasks; usi ...
- 22_微信小程序开发-BLE低功耗蓝牙开发-源码
我没想到很多同学都在私信我,问我要这个 iBLETool 的源码,是我没考虑到的.其实最开始没有把源码分享出来,主要的原因是这个项目是我自己拿来练手的,有很多地方考虑不完善,怕误导了各位同学!现在很多 ...
- Android 蓝牙开发(一) -- 传统蓝牙聊天室
Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 一.蓝牙概览 以下是蓝牙的介绍,来自维基百科: 蓝牙(英语: ...
- Android BLE低功耗蓝牙开发
啦啦啦在上一个项目中有用到BLE低功耗蓝牙开发,当时baidu google了很多资料,但大多数都是千篇一律,英文文档我这种渣渣又看不懂...总之刚开始查的很痛苦.所以要把自己的踩坑之路写下来记录下, ...
- android studio蓝牙低功耗,arduino ESP32 AndroidStudio BLE低功耗蓝牙 物联网
arduino ESP32 AndroidStudio BLE低功耗蓝牙 物联网 nodered开发: esp32采用的蓝牙于普通的蓝牙不同,是低功耗蓝牙,手机用一般的蓝牙代码是连不上的.在本文中,不 ...
最新文章
- 机器学习发现了地下上千公里处的热岩床
- 丛林谜题JAVA_丛林王座图文全剧情流程攻略_全谜题解答通关流程_3DM单机
- 【杂谈】三人行必有AI,你会在其一吗?
- 论文小综 | 文档级关系抽取方法(下)
- 其实Go 1.17 就支持泛型了,具体该怎么用呢?
- 三大运营商一季度财报出炉:营收集体下降
- POJ1082 Calendar Game
- wilcoxon符号秩检验matlab,符号检验和wilcoxon符号秩检验的区别
- msxml6_x86.msi和msxml6_ia64.msi和msxml6_x64.msi的选择
- 哈夫曼树的构造与哈夫曼编码
- 前端学习 之 Highcharts各种图形 示例
- Word排版如何快速自动生成目录,简单实用,一看就懂!
- Java SE java基础 求营业额
- 2.2 PB-ADV入网过程
- 生物信息学python常用脚本_生物信息学一些基本的常用软件有哪些?
- 腾讯 WXG 后台开发工程师对 MySQL 索引知识点总结
- 【收藏】最全计算机网络基础思维导图
- webrtc 的回声抵消(aec、aecm)算法简介
- 阿里云手动更新dns解析
- 深入理解Java之线程池
热门文章
- 下载!微软出品的最新 Kubernetes 学习指南 3.0
- 失踪人口回归微信小程序毕业设计(二)
- [书目20090503]time power 时间力 作者(美国)(brian tracy)博恩.崔西
- 【论文笔记】Commonsense Knowledge Aware Conversation Generation with Graph Attention
- 浏览器缓存 HTTP缓存-CDN缓存-localstorage/sessionstorage/cookie
- 中国航天日,主题是“格物致知,叩问苍穹”
- python读取nii文件_读取nii或nii.gz文件中的信息即输出图像操作
- 把TKStudio改造为一个小巧的代码编辑器
- 那个,在你颓唐不安的时候一直陪在你身边的我。
- 两台主机共用一个戴尔显示器之快速切换主机