当前项目虽然已经引入了 MusicPlaybackController 与 PlaybackCoordinator,但真实播放运行时仍然绑定在 LocalMusic.ets:
IjkMediaPlayer 由 LocalMusic 持有。PlaybackRuntime 与 controller actions 由 LocalMusic 在页面生命周期里注册。LocalMusic 为主状态源。EntryAbility 打开播放页时,仍然依赖 PlaybackCoordinator.hasRuntime(),本质上还是在等 LocalMusic 先成为宿主。这意味着当前播放能力只是“入口抽象了一层”,并没有真正脱离 LocalMusic。只要 LocalMusic 还是运行时宿主,播放就不算完全独立。
本轮目标是把“真实播放 runtime + 播放状态源 + 播放器宿主 UI”从 LocalMusic 中完整迁出,建立独立播放宿主。
完成后必须满足:
LocalMusic 只负责内容展示与发起播放请求,不再持有真实播放器。NewIndex 挂载独立 PlaybackHost,成为唯一播放器宿主。PlaybackCoordinator 的 runtime 来源是 PlaybackHost,不再是 LocalMusic。LocalMusic。LocalMusic 的本地列表结构、搜索结构、歌单结构。AppStorage 兼容字段,允许保留必要镜像。采用“独立宿主 + 快照恢复”的轻量宿主方案。
核心思想:
PlaybackHost,挂在 NewIndex.ets。PlaybackHost 接管真实播放运行时、核心播放状态、恢复链路、播放器页面开关和卡片联动。MusicPlaybackController 继续作为统一播放入口。PlaybackCoordinator 继续作为运行时协调层,但 runtime 改由 PlaybackHost 注册。LocalMusic、WebDavMainPage、FindView、PlaylistDetailPage、ChartsCount 等页面全部退化成内容页,只负责组装队列并发起请求。不采用以下方案:
LocalMusic 作为隐藏宿主常驻。这样只是“藏起来的耦合”,不是完全独立。MusicPlaybackController。这样会把 controller 演化成新的超大类,结构上只是把问题平移。职责保持为统一入口层:
PlaybackCoordinator。它不再承担:
继续作为协调层存在:
它不再依赖 LocalMusic 生命周期,而是只认 PlaybackHost 注册的 runtime。
新增独立播放宿主,挂在 NewIndex。
它是本轮的核心新增组件,负责:
IjkMediaPlayer。PlaybackRuntime 与 controller actions。拆完后,PlaybackHost 是唯一真实播放宿主。
包括但不限于:
这些页面迁移后只负责:
MusicPlaybackController.playQueue/playSong。这些页面迁移后不再负责:
本轮完成后必须满足以下硬边界:
LocalMusic 不能再持有 IjkMediaPlayer。LocalMusic 不能再注册 PlaybackRuntime 或 MusicPlaybackControllerActions。LocalMusic 不能再作为 showPlayerView、播放页开关、切歌逻辑、进度推进、当前歌曲、歌词、AVSession、卡片同步的主状态源。LocalMusic 是否已挂载。PlaybackHost 读取状态。LocalMusic 生命周期。强杀后的恢复语义明确如下:
最后一点的原因是:在应用已死场景下,用户从卡片点击播放的意图更接近“恢复并开始播放”,而不是“恢复到暂停态”。
新增独立快照模型 PlaybackSnapshot.ets。
快照至少包含:
queue: 当前播放队列的精简快照。currentIndex: 当前播放索引。currentSongKey: 当前歌曲稳定标识。positionMs: 当前播放进度。playType: 当前播放模式。playlistContext: 队列来源上下文。updatedAt: 最近更新时间。shouldResumeWhenActivated: 是否存在待恢复播放意图。队列内每首歌的快照项只保留恢复所需字段,例如:
filePathidtypenameartistremote_rel_pathwebdav_account_id明确不持久化:
IjkMediaPlayer 内部状态新增 PlaybackSnapshotStore.ets,职责如下:
写入时机由 PlaybackHost 统一控制:
统一恢复流程如下:
PlaybackHost 正常播放过程中持续写入 PlaybackSnapshot。NewIndex 先挂载 PlaybackHost。PlaybackHost 初始化时读取 PlaybackSnapshot,恢复队列、当前索引、当前歌曲和进度到内存,但先不自动播放。PlaybackHost 检测到待执行激活动作后,再开始恢复播放:
positionMs 开始播放NewIndex,负责 runtime 注册和宿主生命周期。PlaybackHost,让迷你播放条和全屏播放页从宿主取状态。LocalMusic。PlaybackHost。目标:
PlaybackHostIjkMediaPlayerNewIndex 挂载独立宿主验收:
LocalMusic 不可见,播放器也能存活PlaybackCoordinator 的 runtime 来源是 PlaybackHost目标:
LocalMusicshowPlayerView/openPlayerViewFromMiniBar/dismissPlayerView 等入口统一收敛到宿主验收:
LocalMusic 页面,也能打开播放页并正常播控目标:
PlaybackSnapshotStorePlaybackRestoreCoordinator验收:
目标:
LocalMusic 只保留内容页逻辑LocalMusic 中剩余宿主级播放状态写入验收:
LocalMusic 不再是播放状态源LocalMusic 不应影响播放器主链路,只会影响本地内容页功能错误处理统一收口在 PlaybackHost 与 PlaybackRestoreCoordinator,不再分散在 LocalMusic。
PlaybackHost ready 后统一消费本轮必须新增统一日志前缀,便于排查恢复链路:
[playback-host][playback-snapshot][playback-restore][playback-activation]至少覆盖以下事件:
优先覆盖纯逻辑与恢复决策,不强求首轮就完整自动化播放器真机链路。
建议新增 Hypium 或纯逻辑测试:
PlaybackSnapshotStore
PlaybackRestoreCoordinator
MusicPlaybackController
若播放器行为难以自动化,至少把“恢复决策”“快照解析”“索引修正”拆成纯函数做单测。
必须手工验证以下场景:
LocalMusic 页面,也能从排行榜、发现页、歌单详情发起播放LocalMusic 不可见时仍正常LocalMusic 打开后不会重复注册 runtime,也不会重新抢占宿主状态只有以下条件全部成立,才算完成“播放完全独立”:
LocalMusicLocalMusic 不再是播放状态源NewIndex + PlaybackHost 成为唯一播放器宿主LocalMusic本设计是对已有两份设计文档的收束与推进:
LocalMusic。本次设计的定位是:在现有 controller/coordinator 基础上,补齐“独立宿主 + 强杀恢复 + 内容页彻底退化”这三个关键缺口,完成真正意义上的完全独立。