Files
Obsidian/项目文档/同力/蓝牙/蓝牙模块整体流程与开发指南.md
2026-11-05 14:35:04 +08:00

841 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 通力蓝牙模块整体流程与开发指南
> 代码仓库:`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 | 理解页面如何消费状态 |