Android蓝牙BLE(BlueTooth BLE)使用流程及爬坑指南
  AID46dc6Brjf 2023年11月02日 85 0

       项目要用到蓝牙ble进行通信,要求初次使用配置简单,后续使用无感知,稳定接收蓝牙服务方发送的数据,本来以为相对简单,真正调试才发现坑很多,网上找的几个文章都各自有不完善的地方,在此记录下

1.蓝牙BLE的简介

1.蓝牙ble介绍

  蓝牙BLE是在Android4.3系统及以上引入的,但是仅作为中央设备,直到5.0以后才可以既作为中央设备又可以作为周边设备。也就是5.0系统以后,可以手机控制手机了,不过绝大多数的场景手机还是作为中央设备去控制其他的周边设备。Android BLE 使用的蓝牙协议是 GATT 协议。关于这个GATT协议,我就不详细给大家介绍了,放上个链接,感兴趣的可以看一下http://blog.chinaunix.net/uid-21411227-id-5750680.html

2.Service和Characteristic

Service是服务,Characteristic是特征值。蓝牙里面有多个Service,一个Service里面又包括多个Characteristic,具体的关系可以看图

Android蓝牙BLE(BlueTooth BLE)使用流程及爬坑指南_蓝牙ble

图中画的比较少,实际上一个蓝牙协议里面包含的Service和Characteristic是比较多的 ,这时候你可能会问,这么多的同名属性用什么来区分呢?答案就是UUID,每个Service或者Characteristic都有一个 128 bit 的UUID来标识。Service可以理解为一个功能集合,而Characteristic比较重要,蓝牙设备正是通过Characteristic来进行设备间的交互的(如读、写、订阅等操作)。

3.server与client  

 低功耗蓝牙的设备可以分成两类:一类是中央设备,用于找到并与外围设备进行交互,在常见的场景中,一般它具有丰富的功能和复杂的用户界面,比如手机。中央设备在BLE中一般扮演的角色是client。   低功耗蓝牙应用的另一类是外围设备,向中央设备提供信息和服务。在常见的场景中,它一般是搭载在各设备上的蓝牙芯片,扮演server角色。   类似于互联网的HTTP协议一样,属性协议其本质上也是也是一种无状态协议,它不仅在连接时没有状态,在连接和连接之间也没有状态。因此,在每次信息发生变化时,都需要其中一方主动发送信息。

4.交换MTU请求   

在低功耗蓝牙连接中,属性协议默认的MTU长度为23字节。按照1个字节的类型操作码(六种基本操作:请求、响应、命令、指示、确认、通知)以及最少2个字节操作句柄(16BitsUUID)算,数据传输字节最多不超过20字节。在两个设备连接初期,谁也不知道对方底细,因此数据交换严格按照默认MTU来,即MTU为23字节。   如果设备想要发送更大的数据包,那么它就要协商一个更长的MTU。只有客户端可以发起这种请求。客户端的请求包含客户端接收的MTU长度;服务器请求则包含服务器接受的MTU长度。对于同时是是客户端服务器的设备而言,二者提供的接收MTU长度中较小的那个即是连接将会使用的MTU长度,目前最长支持设置517个字节,超过会自动断开连接。


2.蓝牙ble连接流程

1.业务流程

Android蓝牙BLE(BlueTooth BLE)使用流程及爬坑指南_低功耗蓝牙_02

2.连接流程

Android蓝牙BLE(BlueTooth BLE)使用流程及爬坑指南_蓝牙连接_03

3.蓝牙设备搜索
 //////////////////////////////////  搜索设备  /////////////////////////////////////////////////
    private void searchBtDevice() {
        if(bleManager == null){
            Log.d(TAG, "searchBtDevice()-->bleManager == null");
            return;
        }

        if (bleManager.isDiscovery()) { //当前正在搜索设备...
            bleManager.stopDiscoveryDevice();
        }

        if(lvDevicesAdapter != null){
            lvDevicesAdapter.clear();  //清空列表
        }

        //开始搜索
        bleManager.startDiscoveryDevice(onDeviceSearchListener,15000);
    }

    //扫描结果回调
    private OnDeviceSearchListener onDeviceSearchListener = new OnDeviceSearchListener() {

        @Override
        public void onDeviceFound(BLEDevice bleDevice) {

        }

        @Override
        public void onDiscoveryOutTime() {

        }
    };


4.蓝牙连接
  bleManager.connectBleDevice(mContext,curBluetoothDevice,10000,onBleConnectListener);
  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,true,bluetoothGattCallback);
  }
  mBluetoothGatt.connect();
5.连接设置
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);

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.requestMtu(517);
//设置发现服务超时时间
mHandler.postDelayed(serviceDiscoverOutTimeRunnable,MAX_CONNECT_TIME);

if(onBleConnectListener != null){
onBleConnectListener.onConnectSuccess(gatt,bluetoothDevice,status);   //连接成功回调
}
}

  @Override
  public 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 - 3
  Log.w(TAG,"设置MTU成功,新的MTU值:" + (mtu-3) + ",status" + status);
  gatt.discoverServices();
  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设置失败
}
}

  List<BluetoothGattService> services=gatt.getServices();
  String servUuid=services.get(services.size()-1).getUuid().toString();
  String readUuid=services.get(services.size()-1).getCharacteristics().get(0).getUuid().toString();
  String writeUuid=services.get(services.size()-1).getCharacteristics().get(1).getUuid().toString();
  //移除发现服务超时
  mHandler.removeCallbacks(serviceDiscoverOutTimeRunnable);
  Log.d(TAG,"移除发现服务超时");

Log.d(TAG,"发现服务");
//if(setupService(gatt,serviceUUID,readUUID,writeUUID)){
//配置服务信息
if(setupService(gatt,servUuid,readUuid,writeUuid)){
if(onBleConnectListener != null){
onBleConnectListener.onServiceDiscoverySucceed(gatt,gatt.getDevice(),status);  //成功发现服务回调
}
}else{
if(onBleConnectListener != null){
onBleConnectListener.onServiceDiscoveryFailed(gatt,gatt.getDevice(),"获取服务特征异常");  //发现服务失败回调
}
}
            
            

3.踩坑记录

1.连接失败

       1.权限问题

       2.蓝牙连接兼容问题

  bleManager.connectBleDevice(mContext,curBluetoothDevice,10000,onBleConnectListener);
  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,true,bluetoothGattCallback);
  }

2.字节接收只有40个字节

   默认只能接收40个字节,多余40个字节的会截断,不会报错,如果修改需要onConnectionStateChange后设置gatt.requestMtu(517)

 注意:如果设置MTu,设置生效后(onMtuChanged)再发现服务gatt.discoverServices(),否则会不生效

3.蓝牙发现搜索

 如果蓝牙已连接,再次搜索可能会搜索不到,如果需要切换页面并获取蓝牙连接状态时需要注意下,根据情况断开再连接或者传递 device监测是否已连接

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

AID46dc6Brjf