第2章:MVVM架构分层与组件定义

2.1 边界划分原则

MVVM 在播放器状态管理中的核心价值是职责分离:View 只做界面渲染与用户事件采集,ViewModel 持有状态并协调业务逻辑,Model 管理播放器原生能力与持久化数据。三层之间通过单向依赖约束边界——View 依赖 ViewModel,ViewModel 依赖 Model,Model 不反向依赖任何上层。这一强制方向防止了状态管理中的循环引用和职责扩散。

2.2 三层边界定义与职责清单

层次边界范围核心职责禁止行为
ViewUI 组件树、布局、动画、触摸事件数据绑定展示、用户交互转发、生命周期感知直接操作播放器、修改状态、发起网络请求
ViewModel状态容器、业务编排、事件转换管理播放状态(播放/暂停/进度/错误)、格式化数据、调度 Model 接口持有 Context、直接引用 View 实例、执行 IO 操作
Model播放器核心、网络/存储、系统资源提供播放器控制 API、持久化播放记录、触发底层状态回调持有 ViewModel 引用、涉及 UI 线程、直接发送 UI 事件

2.3 依赖关系与信任链路

依赖方向决定了数据流通路:

  • 构造期:ViewModel 通过构造函数获得 Model 接口(例如 IPlayerModel),View 通过依赖注入或工厂获得 ViewModel 实例。
  • 运行期:View 通过 LiveData / StateFlow 订阅 ViewModel 的可观察状态;ViewModel 通过 suspend 函数或回调调用 Model 方法。
  • 信任链路:Model 仅产生不可变状态快照,ViewModel 对状态进行校验与缓存,View 仅作纯渲染。任何对状态的非预期修改都必须追溯到 ViewModel 的 mutation 入口。

2.4 播放器场景下的选型权衡

为何 MVVM 优于 MVC 或 MVP?

  • MVC 中 Controller 与 View 耦合紧密,播放器暂停/继续等高频状态更新易导致 View 层臃肿。
  • MVP 中 Presenter 持有 View 接口,单元测试需 mock View,且 Presenter 生命周期难以独立于 Activity。
  • MVVM 通过数据绑定解耦 View 与 ViewModel,ViewModel 可完全脱离 Android 框架测试;StateFlow/Flow 天然支持响应式状态,适合播放器连续进度、缓冲百分比的实时推送。

2.5 风险点与落地步骤

风险点一:状态泄露 —— ViewModel 在横竖屏切换或后台运行时仍然存活,其内部的播放器状态(如当前播放位置)若未正确恢复,可能导致 UI 显示错误。解决方案:使用 SavedStateHandle 持久化关键状态。

风险点二:边界模糊 —— 开发者在 View 层直接调用 Model 的播放控制方法,或在 Model 中处理 UI 动画。必须通过 Code Review 和架构检测(如依赖反转规则)强制归位。

落地步骤建议:

  1. 定义 Model 接口:包括播放控制、状态回调、持久化方法。
  2. 实现 ViewModel:编写状态容器(sealed class PlayerUiState)及对应 mutation 方法。
  3. 将 View 层拆分为 Composable / Fragment,通过数据绑定订阅 StateFlow。
  4. 编写单元测试:验证 ViewModel 对 Model 调用的正确性和状态转换逻辑。

2.6 验收标准

  • View 层中不存在任何 player.play()、model.loadUrl() 等直接调用。
  • ViewModel 中不存在 context、view 相关引用,且所有播放器操作通过 Model 接口。
  • Model 层不引用 ViewModel 类或接口,也不导入任何 UI 包。
  • 当 View 被销毁重建时,ViewModel 存活且状态不丢失(验证横竖屏切换前后的播放进度一致)。

2.7 分层依赖与数据流结构

下图展示了 MVVM 三层之间的边界、数据流方向及信任链路。View 仅通过绑定消费状态,ViewModel 负责将用户意图转发至 Model,Model 完成后回调状态更新。

MVVM 播放器状态管理分层依赖关系 MVVM 三层边界与数据流 View UI 组件 数据绑定 事件采集 生命周期 ViewModel 状态容器 (StateFlow) 业务编排 事件转换 校验与缓存 Model 播放器控制 API 网络 / 存储 持久化记录 底层回调 用户动作 控制调用 StateFlow 状态通知 (suspend/callback) 信任链路要点 • 数据流单向:Model → ViewModel → View • 控制流单向:View → ViewModel → Model • 不允许反向依赖,状态变更仅通过 ViewModel mutation 入口

实践提示:在 ViewModel 中,将每个播放器状态(如 isPlaying、positionMs、bufferedPercent)封装为独立的 StateFlow,避免将整个大对象推送导致的 View 层冗余刷新。Model 层的回调应当运行在指定的调度器(如 Dispatchers.IO),ViewModel 通过 viewModelScope 收集并转换到主线程。