BLE蓝牙坑爹集锦 一

一年多了,没有来写文章,今天恰逢风高气爽,总结下个人对BLE蓝牙一些问题

image.png

什么是经典蓝牙,什么是BLE蓝牙,什么是双模蓝牙

image.png
A、经典蓝牙:

1)、传声音:如蓝牙耳机、蓝牙音箱。蓝牙设计的时候就是为了传声音的,所以是近距离的音频传输的不二选择。现在也有基于WIFI的音频传输方案,例如Airplay等,但是WIFI功耗比蓝牙大很多,设备无法做到便携。因此固定的音响有WIFI的,移动的如耳机、便携音箱清一色都是基于经典蓝牙协议的。

2)、传大量数据: 例如某些工控场景,使用Android或Linux主控,外挂蓝牙遥控设备的,可以使用经典蓝牙里的SPP协议,当作一个无线串口使用。速度比BLE传输快多了。

B、BLE蓝牙:

1)、耗电低,数据量小,如遥控类(鼠标、键盘),传感设备(心跳带、血压计、温度传感器、共享单车锁、智能锁、防丢器、室内定位)。

2)、目前手机和智能硬件通信的性价比最高的手段,直线距离约50米,一节5号电池能用一年,传输模组成本便宜,远比WIFI、4G等大数据量的通信协议更实用。虽然蓝牙距离近了点,但胜在直连手机,价格超便宜。以室内定位为例,商场每家门店挂个蓝牙beacon,就可以对手机做到精度10米级的室内定位,将来的蓝牙5.1更可以实现厘米级室内定位。

C、双模蓝牙:

1)、智能电视遥控器:很多智能电视配的遥控器带有语音识别,需要用经典蓝牙才能传输声音。而如果做复杂的按键,例如原本键盘表上没有的功能,经典蓝牙的HID按键协议就不行了,得用BLE做私有协议。

2)、降噪耳机:很多降噪耳机上通过APP来调节降噪效果,也是通过BLE来实现的私有通信协议。

开发过程中遇到的问题

1、已配对的列表消失问题
  • BluetoothAdapter.getDefaultAdapter().getBondedDevices();此方法是获取设备已经pair过的列表,有时候没有去removeBond,明明有一个设备,但是某种情况下消失,也就是 getBondedDevices().size=0

目前倾向于是系统做了一些操作

2、在扫描BLE设备的时候,有几率会发现由于BLE地扫描导致地整个App页面卡
3、进行BLE的基本操作导致BLE Crash问题
4、关于不断连接BLE的时候,底层报错status =133的问题
  • 复现方法:不断地disconnect 和 connect
  • 补充说明:连接BLE,假如BLE已经connect到另外的设备,这个时候status 133,是正常现象,但是我们的平板发生的概率很大
5、在蓝牙已经打开的状态下,开启蓝牙扫描的时候,getBluetoothLeScanner()==null,然后设备不能开启扫描
6、蓝牙的建立拦截的过程中,发生无响应的状态
  • 蓝牙连接过程的说明:开始BLE扫描-->获取到目标设备-->开始pair-->成功--->开始连接-->连接成功--->changeMTU-->onMtuChanged() 这个是GattCallback的内置方法,回调--->接着开始 gat.discoverServices();--->秘钥协商--->真正的连接状态
  • 发现问题:当手动调用 changeMTU 后,onMtuChanged方法不回调,就卡在中间

开发中优化的建议

  • 1、蓝牙扫描设备
    我实现的方式是通过开启一个线程池,这个是Executors.newSingleThreadExecutor(),
   /**
     * How to understand:
     * 1. A new task will be placed in corePool, the core thread pool, the size is 1, where corePoolSize=maximumPoolSize=1
     * 2. When the core thread pool is full, it will be placed on the LinkedBlockingQueue. If the LinkedBlockingQueue is a bounded queue, a bounded blocking queue composed of a linked list structure, and it is also full. size=Integer.MAX_VALUE
     * 3. Create a new task in the thread pool smaller than the maximum until the maximum thread pool is full
     * 4. It will go to the rejection queue to see what kind of rejection plan it is, and then just like that
     * 5. Because defaultHandler = new AbortPolicy(); it is possible to throw an exception, although this chance is small
     * 6. The execution time of the execute method is usually a slow process. If it is a fast process, you will not be placed in the thread pool, so I think this is suitable for download initialization
     */


/**
      * 如何理解:
      * 1.会在corePool,核心线程池中放置一个新任务,大小为1,其中corePoolSize=maximumPoolSize=1
      * 2.当核心线程池满时,会放到LinkedBlockingQueue上。 如果LinkedBlockingQueue是一个有界队列,一个由链表结构组成的有界阻塞队列,它也是满的。 大小=整数.MAX_VALUE
      * 3.在线程池中创建一个小于最大值的新任务,直到最大值线程池满
      * 4.它会去拒绝队列看它是什么拒绝计划,然后就这样
      * 5. 因为 defaultHandler = new AbortPolicy(); 有可能抛出异常,虽然这个机会很小
      * 6.execute方法的执行时间通常是一个缓慢的过程。 如果是快进程,你就不会被放到线程池中,所以我觉得这个适合下载初始化
      */
code
  • 2、在实际过程中,BLE发生过假连接的情况,所以判断BLE是否是在连接的状态很关键
    主要是通过 反射去获取连接状态 Method method = bluetoothDeviceClass.getDeclaredMethod("isConnected", (Class[]) null);

  • 3、 BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner()
    在BLE关闭的状态下,这个会null,但是我在开发过程中其实也发现了,也会为null,所以需要判断null,在扫描设备的时候也要主要判断,要不然,整个子线程crash了,都没有反应

  • 4、关于一直扫描Pair的问题,由于安卓系统各不相同,所有在某些板子上有可能发生一直Pair的情况,
    个人建议如果Pair失败的次数过多的情况下,可以偷偷的打开和关闭BLE即可解决问题

  • 5、蓝牙的建立拦截的过程中,发生无响应的状态
    可以偷偷的打开和关闭BLE即可解决问题,具体原因我大约知道,但是不敢肯定,所以不贴原因了

  • 6、关于判断是否BLE连接上了,我看过好多开源的工程都在这个方法中判断onConnectionStateChange
    但是其实不太好,说下我的理由,在这里判断连接失败是可以的,但是呢?连接成功呢,如果项目不涉及到秘钥协商的东西,那应该可以,但是 ,我从事过的商业项目都是加密的点对点的加密,所以当一个完善的项目应该是秘钥协商之后

  • 流程 onConnectionStateChange--> changeMTU(int) --->onMtuChanged()--> gatt.discoverServices()-->onCharacteristicRead()--->根据最后读取的特征值去判断,因为商业项目有可能会读取多个硬件信息,版本号,等等,连接成功,可以正常通讯

  • 7、gatt 不用了需要close,而不是disconnet

  • 8、如果你正在实现一个BLE模块,建议使用状态模式去实现代码,但是也特备要注意下状态管理不能够乱,如果乱了,状态模式反而比较麻烦

  • 9、重连通过 connectByName Or connectByAddress 这种方法去获取到的BluetoothDevice其实地址是同一个,所以直接使用BluetoothDevice去connect即可,不要花花肠子那么多

  • 10、一般情况下,BLE设备有恢复出厂设置,假如这个时候使用虚拟mac地址的,代码需要更多的判断,所以项目维持一个目前连接的设备,当然这种的没有考虑到mesh,如果mesh那会更加的复杂

  • 11、BLE自动重连,在实际过程中,如果BLE断电然后上电,是需要重新连接的,但是建议做个延迟,因为我和硬件工程师联调的时候,发现App重连的指令太快,导致的秘钥协商不通过的问题

  • 12、BLE回调的所有的消息其实都在一个线程池中,所以如果特别的场景,需要考虑到多线程同步的问题。。。

  • 13、status =133 是个正常的现象,如果概率很大,需要做处理

  • 14、2021年9月15日晚上:
    我发现一个现象:
    BLE 移除已经 配对列表过程 :removeBond -- ---->收到系统广播 BOND_NONE (从开始到这一步 大约有4s)
    如果在removeBond 和 收到系统广播中间,以很快的速度去 pair 设备,那么必定发生pair不成功,只有重启才能解决
    新的问题,有点意思,不知道是不是系统的问题Or其他的问题

    '

  • 2022.5.9 android蓝牙多次后,android – 如何防止BluetoothGattCallback一次多次执行
    https://stackoverflow.com/questions/33274009/how-to-prevent-bluetoothgattcallback-from-being-executed-multiple-times-at-a-tim

  • ---- Updated Answer ----

  • I have found that its better to call bluetoothGatt.close() inside the onConnectionStateChanged callback. When you issue a disconnect it sends a message to the bluetooth device to request disconnect. Then once it responds you get the callback and close the bluetooth gatt connection. By waiting for the callback and not opening another gatt connection until its fully closed it seems to prevent multiple gatt objects from getting connected to the app.

其实就是需要在disconnect后,首先调用 GATT.colse()

  • 2022.6.10 ,onMtuChanged status的状态异常的问题
image.png
  • 1.手机我测试到的问题是 onMtuChanged mtu 23 status 10 ,由于 BluetoothGatt.GATT_SUCCESS ==0
  • 2.所以走不到 gatt.discoverServices(); 那么连接的状态也会中断,这个也应该是平板遇到的问题
  • 这里要怎么处理呢?
    • 建议要处理异常?不能仅仅只处理成功的状态
      • 为什么会导致这个现象 ,以下是我的猜测
        1. 是我在频繁的使用手机 connect
      • 2.本来BLE 是connect的状态,还在继续connect ,无视 BLE的 状态
  /**
         * 
         * 1.手机我测试到的问题是 onMtuChanged mtu 23  status 10  ,由于   BluetoothGatt.GATT_SUCCESS ==0 
         * 2.所以走不到  gatt.discoverServices(); 那么连接的状态也会中断,这个也应该是平板遇到的问题
         *
         * 这里要怎么处理呢?
         *   要处理异常?不能仅仅只处理成功的状态
         * 为什么会导致这个现象 ,以下是我的猜测
         *   1. 是我在频繁的使用手机 connect 
         *   2.本来BLE 是connect的状态,还在继续connect ,无视 BLE的 状态
         * @param gatt
         * @param mtu
         * @param status
         */
        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            LogUtils.Sming("onMtuChanged mtu " + mtu + "  status " + status);
            BluetoothMachine.this.mtu = mtu;
            // TODO: 2022/4/9  监控手机是否 也存在  mtu 不回调的问题
            if (status == BluetoothGatt.GATT_SUCCESS) {
                gatt.discoverServices();
                ConnectionStateChangeManager.getInstance().notifyDeviceConnectStateObserver(ConnectionState.DISCOVER_SERVICES);
                LogUtils.Sming(" onMtuChanged gatt.discoverServices() ");
            }
        }
  • 如果断开,蓝牙设备,如果在同步的去调用 Method method = bluetoothDeviceClass.getDeclaredMethod("isConnected", (Class[]) null); ,会发现是连接状态,但是其实Gatt 已经关闭了,为什么 ? 2022.5.9号

Tip:

  • 一个函数只做一件事情,它改做的事
    • 一个函数,如果可能,让它保持在20行以内(优秀), 或者保持一个屏的高度以内(不错)
    • 过于深度的if else逻辑问题, 要考虑抽取、分流的方式,并拆开层级
    • 代码块不能粘在一起,关键位置隔行,以及组成代码块 或者 新的函数

你可能感兴趣的:(BLE蓝牙坑爹集锦 一)