共计 2292 个字符,预计需要花费 6 分钟才能阅读完成。
崩溃日志里的真相
这是新手第一次调用 BluetoothUserService 时常见的崩溃日志:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.bt_demo, PID: 12345
java.lang.IllegalArgumentException: Service UUID cannot be null
at android.bluetooth.BluetoothGattServer.addService(BluetoothGattServer.java:123)
at com.example.bt_demo.BluetoothUserService.startAdvertising(BluetoothUserService.kt:47)
关键错误信息直指问题核心——UUID 参数未正确初始化。这个看似简单的错误背后,涉及蓝牙协议栈的底层约束。
解剖 BluetoothUserService 的核心参数
1. UUID:蓝牙服务的身份证
- 标准格式 :必须遵循
0000XXXX-0000-1000-8000-00805F9B34FB的 32 位十六进制格式 - 作用域:
- 服务 UUID 标识整体功能(如心率监测)
- 特征 UUID 定义具体数据(如心率值)
- 典型错误:
- 直接使用字符串
"heart_rate"等非标准格式 - 不同服务间 UUID 冲突
2. 权限体系:Android 的守门人
必须先在 AndroidManifest.xml 声明这些权限:
<!-- 基础蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Android 12+ 需要额外声明 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
防崩溃代码模板(Kotlin 版)
参数预校验方法
fun validateServiceParams(serviceUuid: String, characteristics: Map<String, String>): Boolean {
// UUID 格式校验
val uuidRegex = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
if (!serviceUuid.matches(uuidRegex.toRegex())) {Log.e("BT_PARAM", "Invalid service UUID format")
return false
}
// 特征值非空检查
if (characteristics.isEmpty()) {Log.e("BT_PARAM", "Characteristics cannot be empty")
return false
}
return true
}
安全启动服务示例
fun startBluetoothService(context: Context) {
val serviceUuid = "0000180D-0000-1000-8000-00805F9B34FB" // 心率服务标准 UUID
val characteristics = mapOf("00002A37-0000-1000-8000-00805F9B34FB" to "READ|NOTIFY" // 心率测量特征)
if (!validateServiceParams(serviceUuid, characteristics)) {showToast(context, "参数校验失败")
return
}
try {BluetoothManager::class.java.getMethod("getAdapter").invoke(context.getSystemService(Context.BLUETOOTH_SERVICE))
?.let { adapter ->
// 实际启动逻辑...
}
} catch (e: Exception) {Log.e("BT_START", "Service 启动失败", e)
}
}
日志抓取技巧
通过 adb 过滤关键错误:
adb logcat | grep -E "BluetoothGatt|BT_PARAM|AndroidRuntime"
厂商兼容性避坑指南
| 厂商 | 特殊限制 | 解决方案 |
|---|---|---|
| 小米 | 后台服务需加入自启动白名单 | 引导用户手动设置 |
| 华为 | 广播间隔必须≥100ms | 使用 setAdvertisingParameters 调整 |
| OPPO | 需要动态申请位置权限 | 适配ACCESS_FINE_LOCATION |
线程安全最佳实践
flowchart TD
A[UI 线程] -->| 发起请求 | B{是否主线程}
B -->| 是 | C[切换到 HandlerThread]
B -->| 否 | D[直接执行]
D --> E[回调结果到主线程]
延伸思考:自动化测试方案设计
可以考虑以下验证维度:
1. 参数边界测试(空值 / 超长字符串 / 特殊字符)
2. 多设备并行连接场景
3. 低电量 / 弱信号等异常环境
4. 快速启停服务的压力测试
下次当你看到 BluetoothGatt 相关的崩溃时,不妨先问自己:我的参数真的符合蓝牙协议规范吗?权限系统是否完整?厂商差异是否考虑周全?
正文完
