Files

840 lines
22 KiB
Markdown
Raw Permalink Normal View History

2026-11-05 14:35:04 +08:00
# 通力蓝牙模块整体流程与开发指南
> 代码仓库:`F:\zdCode\tongli`
> 文档目的:帮助开发者理解“蓝牙模块”和“蓝牙音乐”的真实关系,熟悉整体功能链路,后续能独立开发、定位和修改问题。
> 当前版本重点:先讲清楚蓝牙模块底座,再讲蓝牙音乐为什么依赖蓝牙模块。
---
## 0. 先给结论
### 0.1 蓝牙音乐和蓝牙模块是什么关系?
**蓝牙音乐是蓝牙模块里的一个媒体子功能。**
它看起来有独立 App 入口 `YX_BtMusic`,也在主蓝牙 App `YX_BlueTooth` 里有音乐页,但底层不是独立播放器,而是依赖蓝牙模块提供的连接能力。
```text
蓝牙模块 = 蓝牙能力底座
├─ 蓝牙开关
├─ 设备扫描
├─ 设备配对
├─ 手机连接/断开
├─ HFP 蓝牙电话
├─ PBAP 联系人
├─ MAP 消息
└─ A2DP + AVRCP 蓝牙音乐
```
蓝牙音乐必须依赖:
| 依赖 | 作用 |
|---|---|
| 蓝牙连接 | 手机必须先连上 |
| A2DP Sink | 手机音乐声音通过 A2DP 进车机 |
| AVRCP | 控制手机音乐播放/暂停/上一首/下一首,接收歌名 |
| AudioFocus | 和收音机、导航、电话、语音等抢音频焦点 |
| DataCenter | 把音乐状态同步给 Launcher/媒体卡片 |
所以开发时要这样定性:
```text
蓝牙设置模块:负责“手机能不能连上”
蓝牙电话模块:负责“HFP 电话能不能用”
蓝牙音乐模块:负责“连上后手机音乐能不能播”
音频焦点模块:负责“当前该谁出声”
DataCenter:负责“状态给谁看、谁能反控”
```
### 0.2 为什么容易误解为两个模块?
因为代码里有两个入口:
| 入口 | 说明 |
|---|---|
| `YX_BlueTooth\...\BluetoothMusicActivity.java` | 主蓝牙 App 内置蓝牙音乐页 |
| `YX_BtMusic\...\MainActivity.java` | 独立蓝牙音乐 App |
但它们底层都走同一套 SDK:
```text
BtMusicViewModel
-> BtMusicControlManager
-> BtMusicManager
-> BluetoothAvrcpClient / BluetoothA2dpSinkClient
```
所以是:
```text
入口分开
底层同源
能力依赖蓝牙模块
```
---
## 1. 整体架构
### 1.1 一张小图看全局
```mermaid
flowchart TB
A["页面/服务层<br/>YX_BlueTooth / YX_BtMusic"]
B["SDK 门面层<br/>ControlManager"]
C["业务模块层<br/>Setting / Phone / Music"]
D["平台封装层<br/>HFP / A2DP / AVRCP / PBAP / MAP"]
E["Android 蓝牙栈"]
F["DataCenter<br/>DataManager / DataUtils"]
G["Launcher / 媒体卡片 / 其他模块"]
A --> B --> C --> D --> E
C --> F --> G
```
### 1.2 分层职责
| 层级 | 代码位置 | 作用 |
|---|---|---|
| 页面/服务层 | `YX_BlueTooth`、`YX_BtMusic` | Activity、Fragment、ViewModel、Service、Receiver |
| SDK 门面层 | `YX_BluetoothSDK\...\manager` | 给 App 层提供统一 API |
| 业务模块层 | `YX_BluetoothSDK\...\modules` | 组织蓝牙业务逻辑 |
| 平台封装层 | `YX_BluetoothSDK\...\platform` | 封装 Android 蓝牙 Profile |
| 音频焦点层 | `YX_BluetoothSDK\...\audio` | 蓝牙音乐音频焦点、静音、反控 |
| 状态总线层 | `YX_Support` / `YX_DataCenterService` | 跨模块共享蓝牙状态 |
### 1.3 关键工程模块
| Module | 定位 |
|---|---|
| `YX_BlueTooth` | 主蓝牙应用,包含设置、电话、联系人、通话记录、蓝牙音乐页和后台服务 |
| `YX_BtMusic` | 独立蓝牙音乐入口 |
| `YX_BluetoothSDK` | 蓝牙核心 SDK |
| `YX_Support` | `DataUtils`、`DataManager` 等共享工具 |
| `YX_DataCenterService` | DataCenter 状态服务 |
| `YX_Launcher` | 使用蓝牙状态、展示蓝牙卡片、可能反控音乐 |
---
## 2. 蓝牙模块启动主线
### 2.1 启动流程
```mermaid
flowchart TB
A["BluetoothApplication.onCreate"]
B["BluetoothSdkApplication.onCreate"]
C["初始化 ControlManager"]
D["启动 BluetoothService"]
E["BootReceiver 开机启动兜底"]
A --> B --> C
A --> D
E --> D
```
### 2.2 步骤说明
| 步骤 | 文件/类 | 做什么 |
|---|---|---|
| 1 | `BluetoothApplication` | 主蓝牙 App 启动 |
| 2 | `BluetoothSdkApplication` | 初始化 SDK 管理器 |
| 3 | `BtContactsControlManager` | 初始化联系人门面 |
| 4 | `BtSettingControlManager` | 初始化蓝牙设置门面 |
| 5 | `BtPhoneControlManager` | 初始化蓝牙电话门面 |
| 6 | `BtCallRecordsControlManager` | 初始化通话记录门面 |
| 7 | `BtMusicControlManager` | 初始化蓝牙音乐门面 |
| 8 | `BtVoiceControlManager` | 初始化语音蓝牙控制门面 |
| 9 | `BluetoothService` | 处理电话、外部命令、悬浮窗 |
| 10 | `BootReceiver` | 开机后兜底拉起蓝牙服务 |
### 2.3 `BluetoothService` 的角色
`BluetoothService` 不是普通空服务,它是蓝牙电话和外部命令的编排中心。
它负责:
- 注册电话模块监听。
- 注册设置模块监听。
- 注册联系人弹窗相关监听。
- 接收外部拨号、挂断、接听命令。
- 接收方向盘按键。
- 处理来电时电话页/悬浮窗显示。
- 处理自动接听。
- 避让倒车、AVM 等特殊页面。
---
## 3. 蓝牙连接底座
### 3.1 为什么先看连接?
因为其他功能都依赖连接状态。
```text
手机没连上
-> HFP 电话不可用
-> PBAP 联系人不可同步
-> A2DP 音乐没声音
-> AVRCP 控制拿不到歌名/播放状态
```
### 3.2 设置连接模块职责
核心类:`BtSettingManager`
| 功能 | 说明 |
|---|---|
| 蓝牙开关 | 打开/关闭 BluetoothAdapter |
| 设备扫描 | 搜索附近手机或蓝牙设备 |
| 设备配对 | 处理绑定、配对请求、配对状态 |
| 连接手机 | 建立 HFP、A2DP、PBAP、MAP 等 Profile |
| 断开手机 | 断开 HFP、A2DP、PBAP、MAP |
| 查询状态 | 当前是否打开、是否连接、是否连接中 |
| 耳机 A2DP | 支持外部蓝牙耳机相关连接 |
### 3.3 连接手机小流程
```mermaid
flowchart TB
A["用户点击设备"]
B["SettingViewModel"]
C["BtSettingControlManager"]
D["BtSettingManager.connectPhoneDevice"]
E["连接 A2DP + HFP"]
F["连接状态广播/回调"]
G["页面与 DataCenter 刷新"]
A --> B --> C --> D --> E --> F --> G
```
### 3.4 连接手机详细链路
```text
MainActivity / 设置弹窗
-> SettingViewModel 或 BtSettingViewModel
-> BtSettingControlManager.connectPhoneDevice(linkedInfo)
-> BtSettingManager.connectPhoneDevice(linkedInfo)
-> BluetoothA2dpSinkClient.connectA2dp(mac)
-> BluetoothHfpClient.connectHfp(mac)
-> PBAP/MAP 后续连接或同步
-> BluetoothConnectListenerClient 收到状态
-> IBtSettingModuleListener 回调
-> ViewModel LiveData 刷新页面
```
### 3.5 多 Profile 的含义
连接手机不是一个单点动作,而是多个蓝牙 Profile 共同组成。
| Profile | 作用 | 对应功能 |
|---|---|---|
| HFP | Hands-Free Profile | 蓝牙电话、通话音频 |
| A2DP Sink | Advanced Audio Distribution Profile | 手机音乐音频流输入车机 |
| AVRCP | Audio/Video Remote Control Profile | 控制手机音乐、读取歌曲信息 |
| PBAP | Phone Book Access Profile | 联系人、电话本 |
| MAP | Message Access Profile | 消息、短信相关能力 |
### 3.6 断开手机链路
```text
BtSettingControlManager.disconnectPhoneDevice(mac)
-> BtSettingManager.disconnectPhoneDevice(mac)
-> BluetoothHfpClient.disconnectHfp(mac)
-> BluetoothPbapClient.disconnectPbab(mac)
-> BluetoothMapConnectClient.disconnectMap(mac)
-> BluetoothA2dpSinkClient.disconnectA2dp(mac)
-> 连接状态回调
-> 页面刷新
```
---
## 4. 蓝牙电话功能
### 4.1 电话依赖什么?
蓝牙电话依赖 HFP。
```text
手机 HFP 连接成功
-> 车机可以拨号/接听/挂断
-> 可以拿到当前通话状态
-> 可以处理通话音频
```
核心链路:
```text
BluetoothHfpClient
-> BtPhoneManager
-> BtPhoneControlManager
-> BluetoothPhoneViewModel / BluetoothService
-> BluetoothPhoneActivity / 电话悬浮窗
```
### 4.2 拨号流程
```mermaid
flowchart TB
A["电话页/联系人/通话记录/外部命令"]
B["BluetoothPhoneViewModel 或 BluetoothService"]
C["BtPhoneControlManager"]
D["BtPhoneManager"]
E["BluetoothHfpClient.dial"]
F["HFP 通话状态回调"]
G["电话界面刷新"]
A --> B --> C --> D --> E --> F --> G
```
### 4.3 来电流程
```text
手机来电
-> BluetoothHfpClient 收到 HFP 状态
-> BtPhoneManager 转换通话状态
-> BluetoothService.onHfpCallChanged()
-> 判断是否倒车/AVM/前台/自动接听
-> 显示 BluetoothPhoneActivity 或电话悬浮窗
-> BtInfoPushManager 推送 BT_INFO
```
### 4.4 电话开发要重点看哪里?
| 场景 | 优先看 |
|---|---|
| 电话页面 UI | `BluetoothPhoneActivity` |
| 电话页面状态 | `BluetoothPhoneViewModel` |
| 来电弹窗/外部命令 | `BluetoothService` |
| 拨号/接听/挂断 API | `BtPhoneControlManager` |
| 电话业务核心 | `BtPhoneManager` |
| HFP 底层状态 | `BluetoothHfpClient` |
---
## 5. 联系人与通话记录
### 5.1 联系人依赖什么?
联系人主要依赖 PBAP。
```text
手机连接成功
-> PBAP 可用
-> 拉取手机电话本
-> 本地整理
-> 联系人页面展示
```
核心链路:
```text
BluetoothPbapClient
-> BtContactsManager
-> BtContactsControlManager
-> ContactListViewModel
-> ContactsFragment
```
### 5.2 通话记录依赖什么?
通话记录依赖 PBAP/HFP 相关数据。
```text
手机连接成功
-> 通话记录数据可用
-> BtCallRecordsManager 拉取/整理
-> CallHistoryViewModel
-> CallLogsFragment
```
---
## 6. 蓝牙音乐功能
### 6.1 蓝牙音乐的本质
蓝牙音乐不是车机本地播放器,而是“车机控制手机音乐 + 接收手机音频流”。
```text
手机负责播放音乐
车机负责:
1. 通过 A2DP 接收声音
2. 通过 AVRCP 控制播放
3. 通过 AVRCP 获取歌曲信息
4. 通过 AudioFocus 决定是否出声
```
### 6.2 蓝牙音乐必须满足的前置条件
| 前置条件 | 不满足时表现 |
|---|---|
| 手机已连接 | 蓝牙音乐不可用 |
| A2DP Sink 已连接 | 没有手机音乐声音输入 |
| AVRCP 可用 | 按钮可能无效,歌名可能不刷新 |
| AudioFocus 成功 | 有状态但可能没声音 |
| 未被电话/语音/导航抢占 | 可能暂停、静音或降低音量 |
### 6.3 蓝牙音乐两个入口
| 入口 | 文件 | 说明 |
|---|---|---|
| 主蓝牙 App 音乐页 | `YX_BlueTooth\...\BluetoothMusicActivity.java` | 集成在主蓝牙应用 |
| 独立音乐 App | `YX_BtMusic\...\MainActivity.java` | 独立启动入口 |
底层调用一致:
```text
BtMusicViewModel
-> BtMusicControlManager
-> BtMusicManager
-> BluetoothAvrcpClient / BluetoothA2dpSinkClient / BtMusicAudioManager
```
### 6.4 播放控制流程
```mermaid
flowchart TB
A["用户点击播放/暂停/上一首/下一首"]
B["BtMusicViewModel"]
C["BtMusicControlManager"]
D["BtMusicManager"]
E["BtMusicAudioManager 请求焦点"]
F["BluetoothAvrcpClient 控制手机"]
G["手机音乐 App"]
A --> B --> C --> D --> E --> F --> G
```
### 6.5 播放控制详细链路
```text
BluetoothMusicActivity / YX_BtMusic MainActivity
-> BtMusicViewModel.playBtMusic/pauseBtMusic/preBtMusic/nextBtMusic
-> BtMusicControlManager
-> BtMusicManager
-> 播放前 requestAudioFocus()
-> BluetoothAvrcpClient.play/pause/previous/next
-> 手机音乐 App 执行
```
### 6.6 歌曲信息流程
```text
手机音乐元数据变化
-> BluetoothAvrcpClient.onMetadataChanged()
-> BtMusicManager.onMetadataChanged()
-> MusicInfo(title/artist/album/albumUrl/duration)
-> BtMusicViewModel.mLiveDataMusicInfo
-> 蓝牙音乐页面刷新
-> BtInfoPushManager.sendMusicInfoToLauncher()
-> DataManager 写 BT_MUSIC_INFO
-> Launcher 蓝牙音乐卡片刷新
```
### 6.7 播放状态流程
```text
手机播放/暂停状态变化
-> BluetoothAvrcpClient.onPlaybackStateChanged()
-> BtMusicManager.onPlaybackStateChanged()
-> BtMusicViewModel.mLiveDataPlayState
-> 播放按钮刷新
-> BtInfoPushManager.sendMusicStateToLauncher()
-> DataManager 写 BT_MUSIC_INFO
```
### 6.8 A2DP 连接状态流程
```text
BluetoothConnectListenerClient 收到 A2DP 连接
-> BtMusicManager.onA2dpConnect()
-> 如果未获得音频焦点,先 setA2dpMute()
-> BtMusicViewModel.mLiveDataA2dpConnectStatus = true
-> 页面显示已连接
```
断开时:
```text
A2DP 断开
-> BtMusicManager.onA2dpDisconnect()
-> 清理音乐状态
-> BtMusicViewModel.mLiveDataA2dpConnectStatus = false
-> 页面显示未连接
```
### 6.9 音频焦点流程
```mermaid
flowchart TB
A["准备播放蓝牙音乐"]
B["BtMusicAudioManager.requestAudioFocus"]
C{"是否拿到焦点"}
D["A2DP unmute"]
E["允许 AVRCP play"]
F["A2DP mute / pause"]
G["清理或更新 BT_MUSIC_INFO"]
A --> B --> C
C -->|是| D --> E
C -->|否| F --> G
```
焦点事件:
| 事件 | 处理 |
|---|---|
| `AUDIOFOCUS_GAIN` | A2DP unmute,必要时恢复播放 |
| `AUDIOFOCUS_LOSS` | 释放焦点、A2DP mute、暂停、清理音乐信息 |
| `AUDIOFOCUS_LOSS_TRANSIENT` | 临时 mute、暂停 |
| `AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK` | 降低 A2DP 音量 |
### 6.10 电话与蓝牙音乐的关系
电话和蓝牙音乐都属于蓝牙模块,但使用不同 Profile:
| 功能 | Profile |
|---|---|
| 蓝牙电话 | HFP |
| 蓝牙音乐音频 | A2DP Sink |
| 蓝牙音乐控制/歌名 | AVRCP |
来电或通话时,电话通常会影响蓝牙音乐:
```text
来电/通话
-> 电话或系统音频焦点抢占
-> 蓝牙音乐 AUDIOFOCUS_LOSS 或 TRANSIENT
-> A2DP mute / pause
-> 通话结束后按策略恢复
```
---
## 7. DataCenter 状态同步
### 7.1 为什么要有 DataCenter?
蓝牙模块不只给自己页面用,还要给 Launcher、媒体卡片、语音、其他系统模块用。
所以蓝牙状态会写入 DataCenter。
### 7.2 关键状态
定义位置:`YX_Support\src\main\java\com\yaxon\support\utils\DataUtils.java`
| Key | 作用 |
|---|---|
| `BT_INFO` | 蓝牙开关、连接、电话等综合状态 |
| `BT_MUSIC_INFO` | 蓝牙音乐歌名、歌手、播放状态、进度、时长 |
| `MUSIC_BT_CONTROL_STATE` | Launcher 反控蓝牙音乐 |
| `SOURCE_ID` | 当前音源标识 |
### 7.3 电话状态推送
```text
BtPhoneManager / BtSettingManager
-> BtInfoPushManager.sendBtInfo()
-> DataManager.putObject(MEDIA_URI, BT_INFO, btInfo)
-> Launcher / 其他模块读取
```
### 7.4 音乐状态推送
```text
BluetoothAvrcpClient 收到播放状态或歌曲信息
-> BtInfoPushManager.sendMusicStateToLauncher()
-> BtInfoPushManager.sendMusicInfoToLauncher()
-> DataManager.putObject(MEDIA_URI, BT_MUSIC_INFO, btMusicInfo)
-> Launcher 蓝牙音乐卡片刷新
```
### 7.5 Launcher 反控蓝牙音乐
```text
Launcher 写 MUSIC_BT_CONTROL_STATE
-> BtMusicAudioManager.ContentObserver 触发
-> 读取命令 PRE/NEXT/PLAY/PAUSE
-> 调用 BtMusicManager
-> BluetoothAvrcpClient 控制手机
-> 重置 MUSIC_BT_CONTROL_STATE = -1
```
---
## 8. 开发入口速查
### 8.1 蓝牙设置/连接
| 目标 | 优先文件 |
|---|---|
| 页面入口 | `YX_BlueTooth\...\main\MainActivity.java` |
| 设置状态 | `YX_BlueTooth\...\viewmodel\SettingViewModel.java` |
| 设置弹窗状态 | `YX_BlueTooth\...\viewmodel\BtSettingViewModel.java` |
| SDK 门面 | `YX_BluetoothSDK\...\manager\BtSettingControlManager.java` |
| 连接核心 | `YX_BluetoothSDK\...\modules\BtSettingManager.java` |
| 连接广播 | `YX_BluetoothSDK\...\platform\BluetoothConnectListenerClient.java` |
| 扫描配对 | `YX_BluetoothSDK\...\platform\BluetoothDeviceClient.java` |
### 8.2 蓝牙电话
| 目标 | 优先文件 |
|---|---|
| 电话页面 | `YX_BlueTooth\...\main\BluetoothPhoneActivity.java` |
| 电话状态 | `YX_BlueTooth\...\viewmodel\BluetoothPhoneViewModel.java` |
| 来电/外部命令 | `YX_BlueTooth\...\service\BluetoothService.java` |
| SDK 门面 | `YX_BluetoothSDK\...\manager\BtPhoneControlManager.java` |
| 电话核心 | `YX_BluetoothSDK\...\modules\BtPhoneManager.java` |
| HFP 底层 | `YX_BluetoothSDK\...\platform\BluetoothHfpClient.java` |
### 8.3 蓝牙音乐
| 目标 | 优先文件 |
|---|---|
| 主 App 音乐页 | `YX_BlueTooth\...\main\BluetoothMusicActivity.java` |
| 主 App 音乐状态 | `YX_BlueTooth\...\viewmodel\BtMusicViewModel.java` |
| 独立音乐页 | `YX_BtMusic\...\main\MainActivity.java` |
| 独立音乐状态 | `YX_BtMusic\...\mvvm\BtMusicViewModel.java` |
| SDK 门面 | `YX_BluetoothSDK\...\manager\BtMusicControlManager.java` |
| 音乐核心 | `YX_BluetoothSDK\...\modules\BtMusicManager.java` |
| 音频焦点 | `YX_BluetoothSDK\...\audio\BtMusicAudioManager.java` |
| AVRCP | `YX_BluetoothSDK\...\platform\BluetoothAvrcpClient.java` |
| A2DP Sink | `YX_BluetoothSDK\...\platform\BluetoothA2dpSinkClient.java` |
### 8.4 DataCenter / Launcher 卡片
| 目标 | 优先文件 |
|---|---|
| 蓝牙状态推送 | `YX_BluetoothSDK\...\modules\BtInfoPushManager.java` |
| 状态 key | `YX_Support\...\utils\DataUtils.java` |
| 状态读写 | `YX_Support\...\data\DataManager.java` |
| 状态服务 | `YX_Support\...\data\DataManagerService.java` |
| Launcher 展示 | 搜索 `BT_INFO`、`BT_MUSIC_INFO`、`MUSIC_BT_CONTROL_STATE` |
---
## 9. 常见问题定位路径
### 9.1 蓝牙打不开/关不上
```text
BtSettingControlManager.openBluetooth/closeBluetooth
-> BtSettingManager
-> BluetoothAdapter.enable/disable
-> BluetoothDeviceClient 广播
-> IBtSettingModuleListener
-> ViewModel LiveData
-> BT_INFO
```
### 9.2 手机连接失败
```text
BtSettingManager.connectPhoneDevice()
-> 当前是否已有设备
-> connectA2dp(mac)
-> connectHfp(mac)
-> BluetoothConnectListenerClient 状态广播
-> PBAP/MAP 后续状态
-> 页面连接状态刷新
```
### 9.3 来电不弹窗
```text
BluetoothHfpClient
-> BtPhoneManager.onHfpCallChanged
-> BluetoothService.onHfpCallChanged
-> 判断倒车/AVM/前台/自动接听
-> BluetoothPhoneActivity 或悬浮窗
```
### 9.4 蓝牙音乐按钮无效
```text
Activity 点击
-> BtMusicViewModel
-> BtMusicControlManager
-> BtMusicManager
-> BluetoothAvrcpClient
-> 手机音乐 App
```
重点确认:
- AVRCP 是否连接。
- 手机音乐 App 是否支持 AVRCP。
- 当前是否有 AudioFocus。
### 9.5 蓝牙音乐无声音
```text
A2DP Sink 是否连接
-> AudioFocus 是否成功
-> 是否被 setA2dpMute()
-> 是否被电话/语音/导航抢焦点
-> 当前音源 SOURCE_ID 是否正确
```
### 9.6 歌名不刷新
```text
BluetoothAvrcpClient.onMetadataChanged
-> BtMusicManager.onMetadataChanged
-> BtMusicViewModel.mLiveDataMusicInfo
-> 蓝牙音乐页面
-> BtInfoPushManager
-> BT_MUSIC_INFO
-> Launcher 卡片
```
### 9.7 Launcher 反控无效
```text
Launcher 写 MUSIC_BT_CONTROL_STATE
-> BtMusicAudioManager.ContentObserver
-> 命令值是否正确
-> BtMusicManager 是否执行
-> AVRCP 是否控制手机
-> 是否重置为 -1
```
---
## 10. 当前代码风险点
### 10.1 `isA2dpSinkConnected()` 返回值疑似错误
现象:
```text
BtMusicControlManager.isA2dpSinkConnected()
调用了 mBtMusicManager.isA2dpSinkConnected()
但没有 return 结果
```
同类问题也出现在 `BtMusicViewModel.isA2dpSinkConnected()`。
可能影响:
- 页面一直认为 A2DP 未连接。
- 蓝牙音乐连接状态显示异常。
- 自动播放逻辑异常。
### 10.2 `removeIBtMusicModuleListener()` 参数类型疑似错误
现象:
```text
BtMusicManager.removeIBtMusicModuleListener(IBtSettingModuleListener listener)
```
但音乐监听列表维护的是 `IBtMusicModuleListener`。
可能影响:
- 音乐监听无法正确移除。
- 页面销毁后仍收到回调。
- 重复回调或内存泄漏。
### 10.3 `BtMusicAudioManager` 的 `ContentObserver` 生命周期需要核对
现象:
```text
注册 Launcher 反控 ContentObserver
但需要确认 destroy() 中是否完整 unregister
```
可能影响:
- 内存泄漏。
- 反控命令重复触发。
- 销毁后仍收到状态变化。
### 10.4 `BluetoothService` 的 `ContentObserver` 注销路径需要核对
现象:
```text
onDestroy() 中 unregisterContentObserver(mContentObserver)
但注册路径需要确认是否一定执行
```
可能影响:
- 未注册就注销导致异常。
- 服务销毁异常。
---
## 11. 开发建议
### 11.1 保持调用层级
推荐:
```text
Activity / Fragment
-> ViewModel
-> ControlManager
-> Manager
-> Platform Client
```
不推荐:
```text
Activity 直接调用 BluetoothHfpClient
Activity 直接调用 BluetoothAvrcpClient
```
### 11.2 修改蓝牙音乐时同时确认三件事
| 检查项 | 原因 |
|---|---|
| AVRCP 是否可控 | 决定按钮和歌名 |
| A2DP 是否连接 | 决定有没有手机音频流 |
| AudioFocus 是否成功 | 决定车机是否允许出声 |
### 11.3 修改电话时必须同时看 Service
电话页不是唯一入口,下面这些都会走 `BluetoothService`:
- 来电
- 挂断
- 自动接听
- 方向盘按键
- 外部拨号
- 电话悬浮窗
- 倒车/AVM 避让
### 11.4 修改状态展示时必须看 DataCenter
| 状态 | Key |
|---|---|
| 电话/连接状态 | `BT_INFO` |
| 蓝牙音乐状态 | `BT_MUSIC_INFO` |
| Launcher 反控命令 | `MUSIC_BT_CONTROL_STATE` |
---
## 12. 建议读代码顺序
| 顺序 | 文件/类 | 读它的目的 |
|---|---|---|
| 1 | `settings.gradle` | 确认蓝牙相关 module |
| 2 | `BluetoothSdkApplication` | 理解 SDK 初始化 |
| 3 | `BluetoothApplication` | 理解主蓝牙 App 启动 |
| 4 | `BootReceiver` | 理解开机启动 |
| 5 | `BluetoothService` | 理解电话和外部命令编排 |
| 6 | `BtSettingManager` | 理解连接底座 |
| 7 | `BluetoothConnectListenerClient` | 理解连接状态来源 |
| 8 | `BtPhoneManager` / `BluetoothHfpClient` | 理解蓝牙电话 |
| 9 | `BtMusicManager` / `BtMusicAudioManager` | 理解蓝牙音乐和焦点 |
| 10 | `BluetoothAvrcpClient` / `BluetoothA2dpSinkClient` | 理解音乐控制和音频通道 |
| 11 | `BtInfoPushManager` / `DataUtils` | 理解跨模块状态同步 |
| 12 | 各 Activity / ViewModel | 理解页面如何消费状态 |