一、基础流程
请简述 Android 蓝牙开发的基本流程
1. 权限处理:动态申请蓝牙和定位权限(注意Android 12+新权限)
2. 初始化蓝牙适配器:通过BluetoothManager获取BluetoothAdapter
3. 设备发现:- 注册BroadcastReceiver监听ACTION_FOUND- 调用startDiscovery()开始搜索- 通过bondedDevices获取已配对设备
4. 建立连接:- 客户端:createRfcommSocketToServiceRecord()- 服务端:listenUsingRfcommWithServiceRecord()
5. 数据传输:通过BluetoothSocket的InputStream/OutputStream
6. 资源释放:及时关闭Socket和取消搜索
加分项:
-
提到经典蓝牙与BLE的区别(传输速率/功耗/使用场景)
-
强调Android 6.0+需要运行时定位权限
二、高频技术细节
1. 权限适配(重点!)
如何处理不同Android版本的蓝牙权限?
最佳回答:
// 代码示例+解释
val permissions = mutableListOf<String>().apply {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {add(Manifest.permission.BLUETOOTH_SCAN)add(Manifest.permission.BLUETOOTH_CONNECT)} else {add(Manifest.permission.BLUETOOTH)add(Manifest.permission.BLUETOOTH_ADMIN)}// 设备发现需要定位权限add(Manifest.permission.ACCESS_FINE_LOCATION)
}// 检查并申请权限
if (permissions.any { checkSelfPermission(it) != PERMISSION_GRANTED }) {requestPermissions(permissions.toTypedArray(), REQUEST_CODE)
}
关键点:
-
Android 12(API 31)开始必须使用新权限
-
定位权限在搜索设备时必需
2. 连接失败排查
蓝牙连接失败可能有哪些原因?
排查清单:
1. 权限未正确申请(尤其Android 12+)
2. 设备未处于可发现模式
3. UUID不匹配(经典蓝牙默认用00001101-0000-1000-8000-00805F9B34FB)
4. 未在子线程执行连接操作(主线程会阻塞)
5. 设备距离过远或已断开
6. 未调用cancelDiscovery()(正在搜索时无法连接)
三、高阶实战技巧
1. 自动重连机制
如何实现蓝牙断开后自动重连?
解决方案:
// 方案1:定时重试
private fun reconnect(device: BluetoothDevice, retryCount: Int = 3) {var attempts = 0val handler = Handler(Looper.getMainLooper())fun attemptConnect() {if (attempts >= retryCount) returnthread {try {device.createRfcommSocketToServiceRecord(UUID.randomUUID()).use { socket ->socket.connect()// 连接成功...}} catch (e: IOException) {handler.postDelayed({ attemptConnect() }, 5000) // 5秒后重试attempts++}}}attemptConnect()
}// 方案2:监听连接状态(广播接收器)
private val connectionReceiver = object : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {when(intent.action) {BluetoothAdapter.ACTION_STATE_CHANGED -> {val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)if (state == BluetoothAdapter.STATE_OFF) {// 触发重连逻辑}}}}
}
2. 数据传输优化
如何保证蓝牙数据传输的可靠性?
优化策略:
1. 数据分包:大数据拆分为≤512字节的包
2. 校验机制:添加CRC校验或使用协议头尾标记
3. 确认应答:接收方回复ACK确认
4. 超时重传:设置500ms超时未收到ACK则重发
5. 队列管理:使用LinkedBlockingQueue控制发送速率
四、BLE蓝牙补充
说说BLE和经典蓝牙的区别?
对比表(速记关键点):
维度 | 经典蓝牙 | BLE |
---|---|---|
功耗 | 高(~1mA) | 极低(~0.01mA) |
延迟 | 高(~100ms) | 低(~6ms) |
传输速率 | 2.1 Mbps | 0.27 Mbps |
有效距离 | 10米 | 30米 |
典型场景 | 音频传输、文件共享 | 传感器数据、IoT设备 |
BLE核心操作:
// 扫描BLE设备
val scanner = bluetoothAdapter.bluetoothLeScanner
scanner.startScan(scanCallback) // 需实现ScanCallback// 连接GATT
device.connectGatt(context, false, gattCallback)
五、避坑指南
终极话术模板
请描述一个你实现的蓝牙功能模块
在智能硬件项目中,我负责开发了Android端蓝牙通信模块:
1. 架构设计:- 采用MVP分层,蓝牙服务独立为Singleton- 使用RxJava封装异步操作
2. 关键实现:- 实现自动重连机制(指数退避算法)- 设计二进制协议保证数据传输可靠性- 添加心跳包检测连接状态
3. 难点解决:- 解决Android 12权限适配问题- 优化多设备连接时的资源竞争
4. 性能指标:- 传输成功率从85%提升至99.6%- 平均连接时间缩短至1.2秒
-
必问问题:
-
"蓝牙连接为什么要在子线程执行?"
→ 主线程阻塞会导致ANR,连接操作可能耗时较长
-
-
致命错误:
-
忘记调用
cancelDiscovery()
(正在搜索时无法建立连接) -
未处理Android 12+的
BLUETOOTH_CONNECT
权限
-
-
加分回答:
-
提到蓝牙广播的
IntentFilter
需要动态注册 -
强调
BluetoothSocket
需要try-with-resources或手动close
-