Android 蓝牙ble总结

一、术语概念解析

Profile: 蓝牙规范,可以理解为一种配置定义,例如:A2DP、HFP、GAP、SPP,GATT等都是一种蓝牙规范,如GAP定义了两个设备间如何发现和连接对方,参考链接:https://www.jianshu.com/p/8bed221be010。

**角色:**外围设备和中心设备,手机端一般作为中心设备,在AP121前手机只能作为中心设备

**GATT:**在 BLE中,其协议规范为GATT(Generic Attribute Profile:发送和接收很短的数据段的通用规范),GATT协议由若干个Service组成。

**Service:**蓝牙提供的服务,是Characteristic的集合,Service也可以包含Service。

**Characteristic :**在BLE术语翻译为特征,在实际中可以认为一个数据传输通道,是GATT协议的最少逻辑单元。Characteristic 的特性可以通过Descriptor配置。

**Descriptor:**对Characteristic 的描述,如Characteristic 是否可写,可读,是否使能通知等信息


##二、GATT协议在Android中的对应实现
BluetoothGatt:GATT;
BluetoothGattServer:Service
BluetoothGattCharacteristic:Characteristic
BluetoothGattDescriptor:Descriptor


##三、扫描
方式1: API21前

mBluetoothAdapter.startLeScan(mLeScanCallback);  

方式2: API21后

mBluetoothAdapter.getBluetoothLeScanner().startScan(scanCallback);
mBluetoothAdapter.startLeScan(serviceUUid,mLeScanCallback);  
mBluetoothAdapter.getBluetoothLeScanner().startScan(filters,settings,scanCallback);

低功耗蓝牙广播包:数据包结构:由多个AD数据包组成,AD数据包结构:len+type+data
注意:
1 . Api21后对BLE设备的扫描的过滤和设置增强了支持,在此之前只是提供了serviceUUID过滤且没有提供扫描设置,具体的过滤规则和扫描配置参考ScanFilter和ScanSettings类 ,通过观看API21以上的源码,可以看到早期的扫描实现最终使用的也是API 21后的扫描方式.
2. 注意切勿频繁调用扫描接口,过于频繁调用系统会屏蔽扫描操作,且不回反馈给调用者。


##四、连接设备
方式1: API23前

device.connectGatt(context, isAutoContect, bluetoothGattCallback);

方式2: API23后

 device.connectGatt(context, isAutoContect, bluetoothGattCallback,transport);

注意点:

  1. isAutoContect:是指当远端设备有效时是否自动连接,而不是回连,
  2. transport:连接协议,这里填2,在测试中发现,当读取设备的type的值有时候会返回3,所以不能完全依赖系统的type值,直接填2
  3. 连接成功后不能直接读取设备服务,需要通过发现服务的方式的去读取服务,如果不能发现服务可认为设备连接失败。原因:手机系统设置没有对蓝牙设备进行过服务发现操作,系统没有缓存。
  4. 关于连接错误133,提高成功率的办法:
  • 连接操作一定要在主线程
  • 当连接bluetoothGatt断开连接的时候一定要close调,释放系统资源,因为一般的手机最多能维持6个gatt连接
  • 多次连接保证连接的成功率,适宜次数为3次,因为连接3次都不成功的情况,一般需要重启蓝牙或手机且等待时间适宜,

##五、数据读写
数据读写的最终对象都是BluetoothGattCharacteristic
读:characteristic.getValue()——>bluetoothGatt.writeCharacteristic(characteristic);
写:characteristic.setValue(byte [] value)——>bluetoothGatt.readCharacteristic(characteristic);
注意:

  1. ble读写数据的长度为20byte(实际长度为23,3个字节为数据头),虽然API21后可以通过gatt.requestMtu(size)申请数据发送和接收数据的长度,但同时需要硬件设备的支持。
  2. 当接收通知消息的时候,为了保证通知生效的,需要使能通知的characteristic的descriptor。
 for ( BluetoothGattDescriptor descriptor  :  characteristic.getDescriptors()) {
          descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
           bluetoothGatt.writeDescriptor(descriptor);
          }

##六、状态回调处理

 蓝牙的状态回调并不在主线程,基于对蓝牙的操作应在主线程,所以,在封装蓝牙库时时应有一个主线程的handle,将状态回调处理put进handle中处理,以避免出现不可预知的bug。
/* 
   ble设备回调的处理接口
 */
public abstract class BluetoothGattCallback {

    //连接状态改变时回调,连接成功后使用gatt.discoverService()发现连接设备的服务,当断开连接是应使用gatt.close()释放连接
    public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                        int newState) {
    }

     //获取设备的服务,如果服务获取失败,可认为连接是失败
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    }

   /*
   读操作回调
   */
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
                                     int status) {
    }

   /*
   写操作回调
   */
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic, int status) {
    }

   /*
   通知数据更新时回调
   */
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic) {
    }

    
    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
                                 int status) {
    }

    
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
                                  int status) {
    }

     
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
    }

   /*
   蓝牙信号强度改变回调
   */
 
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
    }

  /*
    蓝牙发送、接收的长度改变回调
    */
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
    }
}


##七、数据分包和拼包的处理方法

  1. 分包:蓝牙ble发送的字节为20byte(系统层实际上会发送23byte,有3byte是数据头),在使用的时候可能会有超过20byte时候就需要分包处理,分包处理很简单,简需要发送的数据分成每包20byte发送即可,尾包按实际字节数发送。
  2. 拼包:蓝牙ble接收的字节数一般也为20字节,所以需要做拼包处理,处理方法是开辟一个数组缓冲区,将接收到的数据放进缓冲区,每次存放数据都要检测数据是否接收完毕,对于异常数据做抛弃处理,缓冲区长度应可自动伸缩,避免当需要一次性传输大数据包是要重新修改蓝牙库。

##八、对于发送状态处理的思考

  1. 情况1:协议带CSW回应:对于这种情况处理相对较为简单,对于一个完整发送的数据包进行缓存,设定相应时间为Nms,超时没有响应可以认为该数据包发送失败。
  2. 情况2:协议不带CSW响应:手机端处理发送状态,对发送的每一包数据进行状态检测,具体做法为,将数据封装为一个类,如下,第七点提到数据的分包发送,每次发送一个数据包会在回调onCharacteristicWrite得到发送的状态 ),成功即可认为发送成功,如果状态为失败,则判断整个数据包为发送失败,当状态成功时,检测发送对象是否还有发送数据,没有即认为整个数据发送成功。
  3. 注意点,手机端有两种发送数据的方式:withResponse(系统层有应答发送)和withNoResponse(系统层无应答发送),采用那种发送方式由固件的BluetoothGattCharacteristic决定)。无论是情况1还是情况2,都有可能出现数据发送无响应的情况,所以需要做超时检测处理。
public class DataPacket {
 
    private   Response response;
    
    private List<byte []> data;

    private long time;

   public void setTime(long time){
	   this.time=time;
   }
  
  public long getTime() {
	  return time;
  }

    public void setData(List<byte[]> data) {
        this.data = data;
    }

    public List<byte[]> getData() {
        return data;
    }

    public void setResponse(Response response) {
        this.response = response;
    }

    public Response getResponse() {
        return response;
    }
    
    
    public boolean isHasSendData(){
        return data!=null&&data.size()>0;
    }
    
    public byte[] getSendData(){
        
        if(!isHasSendData()){
            return null;
        }
        return data.remove(0);
    }

}

##九、和经典蓝牙混合使用的注意项

  1. 通过mBluetoothAdapter.startDisCovery()方式扫描蓝牙,在部分高版本手机中会出现将ble也扫描出来,并通过广播发送出来,因此需要增加判断
  2. A2dp/Hfp蓝牙连接注意情况
    a. 通过fetchUuidsWithSdp发现经典蓝牙的服务。此方法不应该调用多次,调用多次会造成部分手机经典蓝牙A2DP/HFP服务连接失败连接
    b.在通过fetchUuidsWithSdp发现经典蓝牙发现服务时,应先读取设备的UUIDS,判断是否有缓存,如果有就跳过服务发现,直接连接服务,不然会出现连接失败。

##十、重连处理

当设备连接成功后,通过SP保存一个设备的地址,用于做回连操作,回连的处理分为以下几种情况
情况1:BLE先断开,即刻回连4.0设备,如果回连失败,提示失败
情况2:通过监听A2DP/Hfp服务的状态回连ble,一般的如果手机离开设备后,或者隔断时间重启设备,系统会自动回连这两个服务,一次应用可根据这两个服务回连ble
情况3:开启蓝牙,监听蓝牙设备开启状态,当开启的时候进行设备扫描,判断扫描结果是否有需要回连的设备,如有就回连设备

注意:上述几种情况可能会同时出现,所以在回连ble的时候需要处理连接状态,如果连接中,则跳过。

十一、 案例说明:车载车机项目:

蓝牙技术点:

蓝牙ble(低功耗蓝牙)、经典蓝牙(A2DP:蓝牙的高级音频传输协议,AVRCP:远程控制规范(A2DP一般包含AVRCP),HFP:通话免提规范,SPP:蓝牙串口通信)、GAP(用于设备发现连接的规范)

项目结构

1. 通信:采用ble或者spp方式,有能力的公司可以采用ble白名单功能,在ble有兼容性的机型上采用spp的通信方式,避免ble造成的兼容。同时如果考虑传输大数据量数据是采用spp,spp的传输速度比ble快很多。

1.连接流程:

   连接方式1:扫描ble设备--- >发现到设备-->根据广播数据包过滤设备-->连接ble(适用单ble应用)
   

  连接方式2:扫描ble设备--- >发现到设备-->根据广播数据包过滤设备-->连接ble同时连接经典蓝牙(hfp/a2dp);注意:如果是两个单模设备组成的假双模设备需要在广播包数据上带上经典蓝牙的地址。
  
  连接方式2补充:经典蓝牙(hfp/a2dp):蓝牙配对--- >发现uuid--->连接hfp/a2dp。
  
  连接方式3:扫描ble设备--- >发现到设备-->根据广播数据包过滤设备-->获取到经典蓝牙地址(通过广播包)--->经典蓝牙配对---->发现uuid--->连接spp/hfp/a2dp(单单数据通信可不连接hfp和a2dp),通过扫描ble连接spp的优点是可以通过ble的广播包进行设备过滤。
  
  注意:以上说的连接方式是基于车机项目的,实际中,设备的经典不一定具备上述所有规范,比如说,有的设备可能没有HFP,这个可以通过蓝牙UUID判断。

 
 数据发送:项目采用的双向协议,采用一问一答的方式,保证了数据的可靠性。分包租包请参考第7节:数据分包和拼包的处理方法。
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值