840 lines
22 KiB
Markdown
840 lines
22 KiB
Markdown
# 通力蓝牙模块整体流程与开发指南
|
||||
|
|
|
|||
|
|
> 代码仓库:`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 | 理解页面如何消费状态 |
|
|||
|
|
|