安卓BLE,头发掉得快

·  阅读 1711

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

image.png

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

A、经典蓝牙:

image.png

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方法的执行时间通常是一个缓慢的过程。 如果是快进程,你就不会被放到线程池中,所以我觉得这个适合下载初始化
      */
复制代码

image.png

  • 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 是个正常的现象,如果概率很大,需要做处理

Tip:

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