Android 蓝牙BLE总结

一、术语概念解析

Profile: 蓝牙规范,可以理解为一种通信协议,例如:A2DP、HFP、GAP、SPP,GATT等都是一种蓝牙规范。

角色:外围设备和中心设备,手机端一般作为中心设备,在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);

注意:Api21后对BLE设备的扫描的过滤和设置增强了支持,在此之前只是提供了serviceUUID过滤且没有提供扫描设置,具体的过滤规则和扫描配置参考ScanFilter和ScanSettings类 ,通过观看API21以上的源码,可以看到早期的扫描实现最终使用的也是API 21后的扫描方式

四、连接设备

方式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) {
    }
}


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

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

情况1:协议带CSW回应:对于这种情况处理相对较为简单,对于一个完整发送的数据包进行缓存,设定相应时间为Nms,超时没有响应可以认为该数据包发送失败。
情况2:协议不带CSW响应:手机端处理发送状态,对发送的每一包数据进行状态检测,具体做法为,将数据封装为一个类,如下,第七点提到数据的分包发送,每次发送一个数据包会在回调onCharacteristicWrite得到发送的状态,成功即可认为发送成功,如果状态为失败,则判断整个数据包为发送失败,当状态成功时,检测发送对象是否还有发送数据,没有即认为整个数据发送成功。
注意点:无论是情况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);
    }

}



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

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

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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值