架构概览¶
1. 适用范围¶
本页描述当前工程的模块边界和启动流程,重点是 Ali 对话链路与 Omni 联调。项目通过 Product Flavor 在编译时选择语音实现;HEADLESS_MODE 决定本地悬浮窗是否挂载。
Headless 不等于删除 UI 模块
当前源码仍包含本地 UI、卡片和对话状态管理。默认 Headless 模式由 OverlayWindowCoordinator 跳过窗口挂载,状态计算与 gRPC 广播继续工作。维护时需要区分“代码存在”和“当前配置展示”。
2. 模块职责¶
| 模块 | 核心职责 | 维护时的入口 |
|---|---|---|
app |
Application、前台 Service、Coordinator、状态消费和 gRPC 桥接 | VoiceAssistantApplication、VoiceAssistantService |
domain |
领域实体、Repository 契约、UseCase | ChatEvent、对话与语音接口 |
data |
Repository 实现、业务路由、会话与事件管理 | AliDialogueRepositoryImpl、A2A、业务 Handler |
adapter |
语音引擎及设备能力适配 | ASR / TTS / KWS 接口与 flavor 实现 |
framework |
Android 框架、网络和外部能力的装配 | Hilt Module、媒体适配 |
common / core |
公共结果类型、工具和 Android 基础能力 | 公共模型、日志与基础配置 |
core:grpc-server |
Omni 服务协议、服务端实现和流会话 | OmniAgentProto.proto、OmniAgentServiceImpl |
core:grpc-lib / core:mock-grpc |
通信与 Mock 联调相关基础设施 | 以各自 Proto 和调用方向为准 |
sdk:voice-sdk |
独立 SDK 接口与集成边界 | SDK 的 AIDL 和接口声明 |
LocalRepo |
媒体、多模态、地图、小红书等业务 SDK | 各 SDK 模块与依赖配置 |
AudioRecord |
音频采集与原生库集成 | 录音输入、ECNR / VAD 相关实现 |
domain 的 Gradle 配置为 Kotlin/JVM,并通过接口描述依赖。运行时由外层实现这些接口,Hilt 在应用层和各 Module 完成装配。
以下图展示主要依赖关系,省略公共工具与部分业务 SDK:
flowchart TD
App[app] --> Data[data]
App --> Domain[domain]
App --> Grpc[core:grpc-server]
App --> Adapter[adapter]
App --> Framework[framework]
Data --> Domain
Data --> Adapter
Data --> Framework
Adapter --> Domain
Framework --> Domain
Domain --> Common[common]
Adapter --> Audio[AudioRecord]
Data --> Sdk[业务 SDK 与适配模块]
3. 启动流程¶
sequenceDiagram
participant App as Application
participant Grpc as Omni gRPC Server
participant Service as VoiceAssistantService
participant Coord as Coordinators
participant Launcher as Omni Launcher
App->>Grpc: startAsync()
App->>Service: startForegroundService()
App->>App: 初始化导航适配器
Service->>Coord: 绑定 scope、监听事件、初始化状态
Service->>Service: 启动 AppCtrlDispatcher 与 GrpcEventBroadcaster
Launcher->>Grpc: 建立订阅和双向流
Note over App,Grpc: 异步启动已派发不代表端口已经监听成功
VoiceAssistantApplication.onCreate() 初始化日志与显示基础设施,然后异步启动 gRPC、预热本地存储、启动语音前台 Service,并初始化导航适配器。
VoiceAssistantService 负责生命周期胶合:绑定 Coordinator 的作用域与回调、连接事件总线、注册控制入口。登录检查在唤醒前置分发中执行;用户未登录时会触发登录请求并消费该次唤醒。
启动问题应分别确认进程、前台 Service、gRPC 监听和语音 SDK 状态,不能仅凭 Application 日志判断整条链路可用。
4. 谁处理什么事件¶
| 组件 | 职责 | 不能据此推断的行为 |
|---|---|---|
KWSCoordinator |
唤醒监听、命令词处理和事件分发 | 有唤醒日志不等于对话服务已连接 |
ASRCoordinator |
独立 ASR 接口的启停编排 | 不代表 Ali 主链路全部走 AliASRAdapter |
DialogueCoordinator |
对话会话的初始化和启停 | SDK 生命周期与 UI 显隐不是同一状态 |
DialogueStateManager |
消费对话事件,维护对话展示状态 | Headless 下仍参与状态处理 |
AppCtrlDispatcher |
业务事件转换为 Launcher 控制消息 | 指令发送成功不等于业务已执行成功 |
GrpcEventBroadcaster |
事件、文本和助手状态推送 | 前端收到状态不代表收到最终业务结果 |
OverlayWindowCoordinator |
本地窗口生命周期与 Headless 分支 | Headless 下无本地窗口是预期行为 |
5. 对话到业务的边界¶
flowchart LR
SDK[Ali 多模态对话] --> Repo[AliDialogueRepositoryImpl]
Repo --> A2A[A2A 业务路由]
A2A --> Handler[领域 Handler]
Handler --> Event[ChatEvent / DialogueEventBus]
Event --> State[DialogueStateManager]
Event --> Ctrl[AppCtrlDispatcher]
Event --> Push[GrpcEventBroadcaster]
Ctrl --> Omni[Omni Launcher]
Push --> Omni
维护业务时从 Handler 与事件映射开始,不要将业务逻辑重复放入 Activity、Service 或渲染组件。新增事件需要同时检查业务执行、状态消费和对外推送三条路径。
6. 接口边界与兼容¶
- Omni 服务监听同设备回环地址
127.0.0.1:50056。电脑联调使用 ADB 转发,不能把它当作可直接远程访问的服务。 - gRPC 的方法存在,不代表已经实现方法名暗示的全部业务;当前状态控制和录音启停的差异见 Omni 接入。
AppCtrlRequest.parameters与text_input承载业务输入,AppCtrlResponse.message是回执说明;领域参数需要同时核对 Handler、Dispatcher 和 Launcher 约定。- Product Flavor 切换发生在构建时,不提供通过运行时配置任意切换供应商的承诺。
7. 源码定位¶
在工程中按下列入口阅读,公开文档仓库本身不包含 Android 源码。
| 问题 | 工程入口 |
|---|---|
| 模块是否参与构建 | settings.gradle.kts |
| 运行变体、Headless、Mock 配置 | app/build.gradle.kts |
| 进程启动 | app/src/main/java/com/jidouauto/voiceassistant/VoiceAssistantApplication.kt |
| 前台 Service 与窗口 | app/src/main/java/com/jidouauto/voiceassistant/service/ |
| Ali 对话与业务执行 | data/src/ali/java/com/jidouauto/voiceassistant/data/ |
| Omni 协议和 Session | core/grpc-server/src/main/ |
8. 常见定位顺序¶
- 明确 APK flavor、构建参数和设备版本。
- 查看 Application / Service 启动与 gRPC 监听日志。
- 确认登录态、录音权限、音频输入和语音连接。
- 沿一次请求检查 ASR 文本、A2A 路由、Handler 结果及推送。
- 最后核对 Launcher 回执和展示;详细命令见调试与验收。