目录

目录

前言

一、相关概念介绍

二、实战开发

三、项目演示

四、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)相关推荐

  1. Android 8.0 BLE 低功耗蓝牙开发记录

    Android 8.0 BLE 低功耗蓝牙开发记录(1-3)--------------(权限申请篇未完待续) 目的:开源博客,希望大家一起修改博客错误地方,共同完善并会鸣谢提供意见的朋友.为大家提供 ...

  2. Android 蓝牙开发(三) -- 低功耗蓝牙开发

    Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 前面已经学习了经典蓝牙开发,学习了蓝牙的配对连接和通信,又通 ...

  3. c# 低功耗蓝牙_Android ble低功耗蓝牙开发-客户端

    什么是BLE(低功耗蓝牙) BLE(Bluetooth Low Energy,低功耗蓝牙)是对传统蓝牙BR/EDR技术的补充. 尽管BLE和传统蓝牙都称之为蓝牙标准,且共享射频,但是,BLE是一个完全 ...

  4. BT传统蓝牙和BLE低功耗蓝牙的区别

    蓝牙3.0及以下为传统蓝牙.   蓝牙4.0以上标准包含两个蓝牙标准,是一个双模的标准,它包含经典蓝牙部分(Classic Bluetooth)和低功耗蓝牙部分(Bluetooth Low Energ ...

  5. Qt低功耗蓝牙系列一(什么是低功耗蓝牙开发,低功耗蓝牙的通信机制原理)

    文章目录 前言 Android 蓝牙 BLE 低功耗蓝牙协议栈简介 蓝牙的选用 BLE 低功耗蓝牙模块具体应用场景 蓝牙灯控方案 BLE 蓝牙智能锁方案 蓝牙 MAC 地址扫描打印解决方案 蓝牙 Me ...

  6. windows 蓝牙程序开发 ble低功耗蓝牙

    1.需要使用到的动态库 Bluetooth Microsoft.Windows.SDK.Contracts 2.需要使用到的命名控件 using System.Threading.Tasks; usi ...

  7. 22_微信小程序开发-BLE低功耗蓝牙开发-源码

    我没想到很多同学都在私信我,问我要这个 iBLETool 的源码,是我没考虑到的.其实最开始没有把源码分享出来,主要的原因是这个项目是我自己拿来练手的,有很多地方考虑不完善,怕误导了各位同学!现在很多 ...

  8. Android 蓝牙开发(一) -- 传统蓝牙聊天室

    Android 蓝牙开发(一) – 传统蓝牙聊天室 Android 蓝牙开发(三) – 低功耗蓝牙开发 项目工程BluetoothDemo 一.蓝牙概览 以下是蓝牙的介绍,来自维基百科: 蓝牙(英语: ...

  9. Android BLE低功耗蓝牙开发

    啦啦啦在上一个项目中有用到BLE低功耗蓝牙开发,当时baidu google了很多资料,但大多数都是千篇一律,英文文档我这种渣渣又看不懂...总之刚开始查的很痛苦.所以要把自己的踩坑之路写下来记录下, ...

  10. android studio蓝牙低功耗,arduino ESP32 AndroidStudio BLE低功耗蓝牙 物联网

    arduino ESP32 AndroidStudio BLE低功耗蓝牙 物联网 nodered开发: esp32采用的蓝牙于普通的蓝牙不同,是低功耗蓝牙,手机用一般的蓝牙代码是连不上的.在本文中,不 ...

最新文章

  1. 机器学习发现了地下上千公里处的热岩床
  2. 丛林谜题JAVA_丛林王座图文全剧情流程攻略_全谜题解答通关流程_3DM单机
  3. 【杂谈】三人行必有AI,你会在其一吗?
  4. 论文小综 | 文档级关系抽取方法(下)
  5. 其实Go 1.17 就支持泛型了,具体该怎么用呢?
  6. 三大运营商一季度财报出炉:营收集体下降
  7. POJ1082 Calendar Game
  8. wilcoxon符号秩检验matlab,符号检验和wilcoxon符号秩检验的区别
  9. msxml6_x86.msi和msxml6_ia64.msi和msxml6_x64.msi的选择
  10. 哈夫曼树的构造与哈夫曼编码
  11. 前端学习 之 Highcharts各种图形 示例
  12. Word排版如何快速自动生成目录,简单实用,一看就懂!
  13. Java SE java基础 求营业额
  14. 2.2 PB-ADV入网过程
  15. 生物信息学python常用脚本_生物信息学一些基本的常用软件有哪些?
  16. 腾讯 WXG 后台开发工程师对 MySQL 索引知识点总结
  17. 【收藏】最全计算机网络基础思维导图
  18. webrtc 的回声抵消(aec、aecm)算法简介
  19. 阿里云手动更新dns解析
  20. 深入理解Java之线程池

热门文章

  1. 下载!微软出品的最新 Kubernetes 学习指南 3.0
  2. 失踪人口回归微信小程序毕业设计(二)
  3. [书目20090503]time power 时间力 作者(美国)(brian tracy)博恩.崔西
  4. 【论文笔记】Commonsense Knowledge Aware Conversation Generation with Graph Attention
  5. 浏览器缓存 HTTP缓存-CDN缓存-localstorage/sessionstorage/cookie
  6. 中国航天日,主题是“格物致知,叩问苍穹”
  7. python读取nii文件_读取nii或nii.gz文件中的信息即输出图像操作
  8. 把TKStudio改造为一个小巧的代码编辑器
  9. 那个,在你颓唐不安的时候一直陪在你身边的我。
  10. 两台主机共用一个戴尔显示器之快速切换主机