经典蓝牙(BR/EDR)开发实战
蓝牙开发面试通关:经典蓝牙(BR/EDR)开发实战
考点定位
面试官常问:“经典蓝牙和低功耗蓝牙有什么区别?你做过哪些经典蓝牙的实际开发?讲讲蓝牙配对流程和SPP通信的坑。”
这个章节在蓝牙开发面试中权重约30%,属于基础必考题。如果你简历写了蓝牙开发,面试官默认你会经典蓝牙——因为这是蓝牙技术的“老本行”,很多物联网、车载、音频设备仍然重度依赖它。常见问法包括:
- 原理类:“BR/EDR的跳频机制是什么?”
- 对比类:“BR/EDR和BLE怎么选?”
- 实战类:“你遇到过SPP连接断开后重连失败吗?怎么排查?”
- 手撕类:“写一个蓝牙设备搜索的伪代码。”
1. 经典蓝牙核心概念:一句话说清楚
经典蓝牙(BR/EDR)是蓝牙技术的第一代标准,主打持续连接、高吞吐、音频传输。它像一条“专线电话”——建立连接后一直占用信道,适合传输语音、文件、打印机数据等。
原理展开
- BR(Basic Rate,基础速率):1 Mbps,最早版本,现在基本被EDR取代。
- EDR(Enhanced Data Rate,增强速率):2-3 Mbps,通过8DPSK调制提升速率,兼容BR。
- 连接模式:点对点,一个主设备最多连7个从设备(piconet)。
- 跳频机制:79个信道,每秒跳1600次,抗干扰强。面试官常问:“为什么经典蓝牙比Wi-Fi抗干扰?”答案就在这——跳频比Wi-Fi的固定信道更灵活。
- 功耗:典型功耗约30-50mA(持续连接),远高于BLE(几μA到mA级)。
面试常考细节
| 细节 | 为什么重要 | 面试官会怎么问 |
|---|---|---|
| ACL(异步无连接)和SCO(同步面向连接)链路区别 | ACL传数据,SCO传语音,面试官会问“蓝牙耳机为什么声音不卡顿?” | “SCO链路怎么保证实时性?” |
| 配对流程的三种方式 | 面试官会问“你遇到过配对失败吗?怎么排查?” | “Just Works和Passkey Entry有什么区别?” |
| L2CAP层的MTU协商 | 实际开发中MTU不匹配会导致丢包 | “你调过MTU吗?怎么确定最佳值?” |
| HCI层命令 | 面试官会问“你用过哪些HCI命令调试?” | “怎么用hcitool查看蓝牙状态?” |
手撕代码:蓝牙设备搜索(Android伪代码)
面试官不会让你写完整App,但会让你写出核心逻辑,比如:
// 经典蓝牙搜索伪代码(考点:权限、回调、搜索超时) BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (!adapter.isEnabled()) { // 注意:这里不能直接enable,需要用户授权 // 面试官会追问:怎么判断用户拒绝授权? return; } // 注册广播接收器(考点:动态注册 vs 静态注册) BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 注意:这里要判断device.getName()可能为null // 面试官会问:为什么有些设备搜不到名字? Log.d("BT", "Found: " + device.getName() + " [" + device.getAddress() + "]"); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { // 搜索结束,可以停止扫描 } } }; registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); // 开始搜索(考点:搜索会消耗大量电量,需要控制时长) adapter.startDiscovery(); // 面试官会问:startDiscovery()和startLeScan()有什么区别?面试话术:“经典蓝牙搜索用startDiscovery(),它会持续12秒,期间会占用射频资源,所以最好在UI上显示进度条,并且搜索结束后及时调用cancelDiscovery()。”
2. 经典蓝牙 vs 低功耗蓝牙:高频对比考点
面试官最爱问:“你什么时候用经典蓝牙,什么时候用BLE?” 直接上表格:
| 对比维度 | 经典蓝牙(BR/EDR) | 低功耗蓝牙(BLE) |
|---|---|---|
| 核心用途 | 持续数据流(音频、文件) | 小数据包、低功耗场景 |
| 功耗 | 高(30-50mA) | 极低(μA级,纽扣电池用几年) |
| 传输速率 | 1-3 Mbps(EDR) | 125 Kbps - 2 Mbps(BLE 5.0) |
| 连接模式 | 持续连接,主从固定 | 广播+连接,可一对多 |
| 信道数 | 79个,跳频 | 40个,其中3个广播信道 |
| 延迟 | 约100ms | 约3-6ms(BLE 5.0) |
| 典型设备 | 蓝牙耳机、车载、打印机 | 智能手环、传感器、Beacon |
| 配对复杂度 | 高(支持多种配对方式) | 低(Just Works为主) |
举一反三:面试官可能会追问:
- “为什么BLE延迟更低?” 因为BLE连接间隔可调(7.5ms-4s),而经典蓝牙固定100ms。
- “BLE能传音频吗?” 传统不能,但BLE Audio(LC3编码)在蓝牙5.2后支持,面试官会问“你知道LC3吗?”
- “经典蓝牙能当BLE用吗?” 双模芯片可以,但功耗降不下来。
面试话术:“选型时,如果设备需要持续传输数据且对延迟不敏感(比如打印机),用经典蓝牙;如果设备靠电池供电且只发送小数据(比如温度传感器),用BLE。如果两者都需要,选双模芯片。”
3. 经典蓝牙配对流程:面试必考
面试官常问:“描述一下经典蓝牙的配对过程,以及可能失败的原因。”
核心概念
配对是建立信任关系的过程,分为三个阶段:
- 配对阶段:交换IO能力(输入输出能力),确定配对方式。
- 链路密钥生成:根据配对方式生成128位密钥。
- 服务发现:配对后,主设备会查询从设备支持的服务(SDP协议)。
三种配对方式
| 方式 | 场景 | 安全性 | 面试官会问什么 |
|---|---|---|---|
| Just Works | 耳机、音箱(无输入) | 低(MITM攻击风险) | “Just Works怎么防窃听?”(答:不能,但SSP有临时密钥) |
| Passkey Entry | 手机配对(有显示屏) | 中 | “Passkey Entry的6位数字怎么生成的?”(答:随机生成,显示在设备上) |
| Numeric Comparison | 两个设备都有显示屏 | 高 | “Numeric Comparison和Passkey Entry有什么区别?”(答:前者用户确认数字是否一致,后者需要输入) |
面试常考细节
- SSP(Secure Simple Pairing,安全简单配对):蓝牙2.1引入,替代了老旧的PIN码配对。面试官会问:“SSP解决了什么问题?”(答:解决了PIN码被暴力破解的问题)
- MITM攻击:面试官会问:“经典蓝牙容易被中间人攻击吗?”(答:Just Works方式有风险,但SSP的Numeric Comparison可以防御)
- 配对失败原因:
- IO能力不匹配(一个设备有显示屏,另一个没有)
- 服务发现超时(SDP查询失败)
- 密钥存储问题(Android的蓝牙共享偏好设置损坏)
面试话术:“实际开发中,配对失败最常见的原因是SDP查询超时。我会先检查设备是否处于可发现模式,然后用hcitool查看SDP记录是否正常。如果设备是自定义固件,可能是SDP记录格式不对。”
4. SPP(串口协议)实战:面试高频
面试官常问:“你用过SPP吗?它和BLE的NUS(Nordic UART Service)有什么区别?”
核心概念
SPP(Serial Port Profile,串口协议)是经典蓝牙最常用的协议之一,它模拟串口通信,让蓝牙像一根串口线一样传输数据。很多嵌入式设备(如GPS模块、打印机)都用它。
原理
- 基于RFCOMM:RFCOMM是蓝牙协议栈中的一层,模拟RS-232串口信号。
- 数据传输:通过L2CAP层封装,最大包长约1000字节(取决于MTU)。
- 连接流程:客户端发起连接 → 服务端接受 → 建立RFCOMM通道 → 双向数据传输。
面试常考细节
| 细节 | 为什么重要 | 面试官会怎么问 |
|---|---|---|
| MTU协商 | 默认MTU可能只有48字节,实际开发需要调大 | “你遇到过SPP传输大文件卡顿吗?怎么解决?” |
| UUID | SPP的UUID是固定的(00001101-0000-1000-8000-00805F9B34FB) | “怎么判断一个设备支持SPP?”(答:查询SDP记录中是否有这个UUID) |
| 流控 | 经典蓝牙没有硬件流控,需要软件处理 | “SPP传输丢包怎么处理?”(答:应用层加CRC校验和重传机制) |
| 多连接 | 一个设备最多支持7个SPP连接 | “你遇到过SPP连接数上限吗?”(答:Android限制同时连接数,通常4-6个) |
手撕代码:SPP客户端连接(Android伪代码)
// SPP客户端连接伪代码(考点:UUID、线程、异常处理) BluetoothDevice device = ...; // 从搜索得到 UUID sppUuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // 注意:连接必须在子线程执行,否则ANR new Thread(() -> { try { BluetoothSocket socket = device.createRfcommSocketToServiceRecord(sppUuid); // 面试官会问:createRfcommSocketToServiceRecord和createInsecureRfcommSocket有什么区别? // 答:前者需要配对,后者不需要(不安全) // 开始连接(超时约30秒) socket.connect(); // 获取输入输出流 InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); // 读取数据(考点:read()是阻塞的,需要单独线程) byte[] buffer = new byte[1024]; int bytes = input.read(buffer); Log.d("SPP", "Received: " + new String(buffer, 0, bytes)); // 注意:连接完成后要取消搜索,否则影响连接稳定性 adapter.cancelDiscovery(); } catch (IOException e) { // 面试官会问:连接失败怎么排查? // 答:检查设备是否可发现、是否已配对、UUID是否正确、是否超出连接数 Log.e("SPP", "Connection failed", e); } }).start();面试话术:“SPP开发最大的坑是线程管理。连接和读写都必须放在子线程,否则会ANR。另外,连接成功后一定要调用adapter.cancelDiscovery(),否则搜索会干扰连接稳定性。我遇到过因为没取消搜索导致连接频繁断开的案例。”
5. 进阶追问:面试官可能深挖的点
如果面试官觉得你基础不错,可能会追问:
“蓝牙音频怎么传输的?”
- 答:通过SCO链路,A2DP协议,编码格式有SBC、AAC、aptX等。面试官会追问“你知道LDAC吗?”(答:索尼的高音质编码,支持990kbps)
“经典蓝牙的功耗怎么优化?”
- 答:减少连接间隔、使用Sniff模式(嗅探模式,降低监听频率)、关闭不必要的服务发现。面试官会追问“Sniff模式怎么配置?”(答:通过HCI命令设置Sniff参数)
“你遇到过蓝牙地址冲突吗?”
- 答:经典蓝牙的BD_ADDR是唯一的,但有些山寨设备会伪造地址。面试官会追问“怎么检测地址冲突?”(答:连接后验证设备名称或服务UUID)
“经典蓝牙和Wi-Fi共存怎么处理?”
- 答:两者共用2.4GHz频段,需要时分复用。面试官会追问“Android怎么处理共存?”(答:通过BT-Wi-Fi coexistence机制,优先保证Wi-Fi传输)
总结:面试话术模板
面试官问“讲讲你做的经典蓝牙项目”时,可以这样回答:
“我做过一个车载蓝牙免提项目,基于经典蓝牙的HFP协议。主要挑战是配对稳定性和音频延迟。配对方面,我遇到设备无法自动配对的问题,后来发现是SDP记录中缺少HFP的UUID。音频延迟方面,我们通过调整SCO连接参数(如voice setting)把延迟从150ms降到80ms。另外,我还处理过SPP连接断开后重连失败的问题,原因是MTU协商不一致——服务端默认MTU是48字节,客户端没做协商,导致大包被丢弃。解决方案是在连接后主动协商MTU到512字节。”
关键点:有具体问题、有排查过程、有解决方案、有数据支撑。面试官要的不是“我会”,而是“我踩过坑,并且爬出来了”。
