# 通力蓝牙模块整体流程与开发指南 > 代码仓库:`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["页面/服务层
YX_BlueTooth / YX_BtMusic"] B["SDK 门面层
ControlManager"] C["业务模块层
Setting / Phone / Music"] D["平台封装层
HFP / A2DP / AVRCP / PBAP / MAP"] E["Android 蓝牙栈"] F["DataCenter
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 | 理解页面如何消费状态 |