BLE 面试题

1、我们知道蓝牙4.0是蓝牙发展史上的最为经典的,你可以说说的特性

  • Bluetooth 4.0 是诞生至今唯一一个综合协议的规范
  • 提出了 低功耗的蓝牙 传统蓝牙 和高速蓝牙的三种模式 ,高速蓝牙其实就是数据传输,传统的蓝牙信息沟通、设备连接,低功率蓝牙,不需要占用太多的带宽的设备连接,低功率的蓝牙,其实是nokia 开发的Wibree技术
  • Bluetooth 4.0 传输距离提升到100米
  • 健壮性有所提升,所有的数据包使用 24bit的 CRC校验 ,确保抵抗干扰
  • 安全提高,使用AES129 CCM加密算法进行数据包的加密和验证
  • 为后面 Bluetooth 4.1 ,Bluetooth 4.2 做了伟大铺垫

2。说说蓝牙4.2, BLE模式下和经典蓝牙的非连接状态下面的延迟,那个会久一点? 经典蓝牙大约 100ms,BLE要 小于6秒

3、最新的蓝牙版本为5.2,最大传输的速度 48M/s 。传输的距离 300米

4、说说BLE的整体连接的步骤

  1. 开始 startScan
  2. 得到想要 connect的设备 ,通过 过滤 public static final int EBLE_MANDATA = 0xFF
  3. bluetoothDevice.connectGatt()
  4. onConnectionStateChange
  5. changeMTU
  6. onMtuChanged 调用 gatt.discoverServices();
  7. 收到 onServicesDiscovered 的 信息,可以去读取一些定义的设备的信息
  8. boolean isSuccess = bluetoothGatt.readCharacteristic(bluetoothGattCharacteristic);
  9. onCharacteristicRead 收到信息,这里处理收到的信息
  10. onDescriptorWrite 中收到回调的信息
  11. 如果 需要秘钥协商的话 就开始 boolean isInitSuccess = bluetoothGatt.setCharacteristicNotification(bluetoothGattCharacteristic, isToTurnOn);
  12. 如果成功的话 onDescriptorWrite() 收到信息 开始写入数据 ,
  13. 收到 onCharacteristicWrite callback 在写入 信息
  14. onCharacteristicChanged 最后收到这个信息的改变
  15. onCharacteristicWrite 收到他的信息 ,秘密协商

[图片上传失败...(image-d644fd-1652069815671)]

5、说说 bluetoothGatt 的 close 和 disconnect 的区别

 public void close() {
if (DBG) Log.d(TAG, "close()");

    unregisterApp();
    mConnState = CONN_STATE_CLOSED;
    mAuthRetryState = AUTH_RETRY_STATE_IDLE;
}

、概述

现在的BLE都可以连接多个蓝牙设备,当我们使用BluetoothDevice.connectGatt()连接设备的时候,自然会想到使用BluetoothGatt.disconnec()断开连接,那么BluetoothGatt类的disconnect()和close()各有什么作用呢?下面我们一起来看一看。

2、disconnect()的作用

从函数名可以看出,disconnect()是用来断开连接的,但是用它断开连接后,连接所占用的资源并没有被释放,而只是暂时的断开连接,可以调用BluetoothGatt.connect()方法进行重连,这样还可以继续进行断开前的操作。但它存在的问题是资源一直被占用,所以多次使用BluetoothDevice.connectGatt()连接就会出现连接不上的情况。这是因为每个蓝牙设备的最大连接次数是有限的,之前连接的资源一直没有被释放,从而导致后面无法连接。在我们的BLE连接不上的时候很可能就是因为这个原因,所以我们需要及时的使用close()来释放资源。

3、close()的作用

close()是用来释放连接所占用的资源即释放BluetootheGatt的所有资源,一旦调用了该方法就不能再使用BluetoothGatt.connect()方法进行重连了,只能使用BluetoothDevice.connectGatt()方法进行重新连接。

4、区别和注意

(1)区别

从上面可以看出如果需要临时断开那就使用disconnect()方法,如果要彻底的断开并释放资源那就使用close()方法。

(2)注意

需要注意的是,这两个方法我们常常搭配着一起使用,一般都是先调用disconnect()方法,此时如果断开成功,会回调onConnectionStateChange()方法,在这个方法中我们再调用close()释放资源。但如果我们使用disconnect()方法后立即调用close(),会导致无法回调onConnectionStateChange()方法。因此我们需要在合适的地方使用close()释放资源。

6、通许过程中,有使用秘钥协商么?什么是秘钥协商?做了那些具体的动作 ,知道什么是DH算法么

DH算法进行秘钥协商,128AES算法用于数据的加密,CRC16用于检验和验证,DH算法就是解决了双方不直接传递秘钥的情况下完成秘钥交换。

[图片上传失败...(image-e40c66-1652069815670)]

[图片上传失败...(image-96cf08-1652069815670)]

简单说下几种蓝牙的架构实现的方式

比如说安卓手机,手机里面包含了很多的SoC(单片系统或片上系统(英语:System on a Chip,缩写:SoC)是一个将电脑或其他电子系统集成到单一芯片的集成电路。单片系统可以处理数字信号、模拟信号、混合信号甚至更高频率的信号

架构一:Host + controller 双芯片的架构

[图片上传失败...(image-14815a-1652069815670)]

  • 蓝牙规格定义了一套标准,使得手机厂商,比如苹果,用一颗新AP替换老AP,蓝牙模块不需要做任何更改;同样用一颗新蓝牙模块换掉老蓝牙模块,AP端也不需要做任何更改。
架构2:单芯片整体方案

[图片上传失败...(image-b8206f-1652069815670)]

  • 手机周边蓝牙设备是蓝牙另外一个非常重要的应用场合,通常手机周边设备功能比较简单,但对成本非常敏感,因此采用一颗芯片来实现整个蓝牙协议栈就是非常明智的选择,即把蓝牙协议栈所有功能都放在一颗芯片上
  • 也就是说,host和controller都放在同一颗芯片上,由于host和controller都在同一颗芯片上,因此物理HCI就没有存在的必要性,host和controller之间直接通过API来交互。
架构3:自定义双芯片架构,一般来说都是蓝牙的设备

[图片上传失败...(image-c11f9d-1652069815670)]

还有一些蓝牙设备功能比较强大,它需要一颗功能非常强大的MCU来做主应用,而蓝牙SoC只是整个系统的一部分,这种情况下,大部分蓝牙协议栈功能或者整个蓝牙协议栈功能都是跑在蓝牙SoC中,而蓝牙应用则跑在主MCU中,主MCU和蓝牙SoC之间的通信协议由厂商自己定义,因此称为自定义双芯片架构方案。

  • 这种方案也非常常见,可以说,除了架构1和架构2之外的架构,都可以称为架构3。架构3里面有一种非常特殊的情况,即主MCU和蓝牙SoC之间采用了HCI接口进行通信,由于这里的HCI只是用来进行物理通信,而通信的主体不是host和controller,通信包应用数据也不遵循蓝牙核心规格规范,因此不能把它看成第1种架构

开发过程中遇到的问题

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其他的问题

    15、如果断开,蓝牙设备,如果在同步的去调用 Method method = bluetoothDeviceClass.getDeclaredMethod("isConnected", (Class[]) null); ,会发现是连接状态,但是其实Gatt 已经关闭了,为什么 ?

    16 、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()

    你可能感兴趣的:(BLE 面试题)