本文是 MAD 收敛计划(
mad-modernization-plan.md)中 Track C 的可落地展开。 Track C 是三轨里唯一长期、且依赖 A、B 产出的一条:它把阅读器的 Canvas View 渲染栈渐进替换为 Compose,用声明式状态 + 本地动画状态取代大部分指令式渲染命令。写作时点:Track A(A1–A5)与 Track B(B1/B2)已落地。B2 已把指令式渲染协议 从 ViewModel 下沉到 UI 层的
ReadBookController(ReadBook.renderCallBack: ReaderRenderCallback)。Track C 从这个边界继续往下走。
2026-07-25:C0–C5 的实现已从代码库删除。以下全文保留为设计记录与性能基线证据, 但描述的代码不再存在,不要按文中路径去找文件。
已删除:
ComposeReaderSurface.kt、ComposeReaderRenderCache.kt、ReaderPageSnapshot.kt、ReaderRenderModel.kt(含ReaderRenderStateStore)、ReaderRenderer.kt(契约 + 两个实现 +ReaderRenderAction)、lab flagLabSettings.composeRenderer全链(PreferKey / DataStore 映射 / LabConfig / 三处字符串)、ReadBookController.publishRenderStructure()及其 12 个调用点、 单测ReaderRenderStateStoreTest/ReaderRendererVerificationTest。保留:
ReaderViewport.kt(C4 的ReaderViewport/ReaderLayoutCoordinator接缝——ContentTextView与ReadBookController在用,非 Compose 专属)、ReaderFirstFrame.kt(debug 首帧探针,tools/capture_reader_c3_first_frame.py依赖)。理由:冻结态并非零成本——每次翻页/内容更新都在
publishStructure里做一遍 无消费方的三页快照投影。维护成本 > parity 实验台价值。若将来性能调查结论支持重启, 按本文设计重新实现即可。
C0–C5 已落地并真机验收;此后暂停,不推进 C6–C9。
依据:C3 真机帧基线显示 Compose 渲染明显劣于旧 View(jank 50% vs 4.13%;50/90/95/99 = 30/42/44/53ms vs 5/8/21/65ms,见 §3 C3),且仿真卷曲/选择/滚动/自动翻页全量重写 = 高回归风险、低用户收益。故上层 MAD 计划改为「保留
ReadView作渲染核心、解其自身业务耦合(Track D)」,Compose 阅读器退为可选。C6–C9:暂缓(非当前主线)。它们描述的仍是「让 Compose 成为默认并删旧栈」的路线;是否重启,取决于将来一次专门的性能调查结论(能否把 Compose 帧耗时/jank 拉到不劣于旧 View、且仿真卷曲有等价观感)。在此之前,不在 overlay 上继续堆图片/选择/动画/滚动——§4 的双栈警告在「暂停」语境下同样成立:不推进,就不该扩大双栈。
上层背景与近期主线 Track D(
ReadView出站解耦)设计见mad-modernization-plan.md(进度快照 ⑥、Track D 节)。
ChapterProvider / TextChapterLayout /
ZhLayout 这套分页排版引擎不重写,其产物(TextChapter→TextPage→
TextLine→*Column)作为绘制数据被 Compose 复用。Track C 重写的是
「绘制 + 手势 + 翻页动画 + 选择」这一层,不是排版。ReadView 栈与新 Compose 栈共存,逐段达成
parity 后再翻默认值、最后删旧栈。任何一步都可回滚。ReaderRenderCallback(B2:ReadBookController 实现)
→ ReadView (自定义 ViewGroup)
├─ PageView × 3(prev/cur/next)
│ └─ ContentTextView(onDraw 里用 Canvas 逐 TextLine/TextColumn 画)
├─ PageDelegate(翻页动画/手势)
│ ├─ CoverPageDelegate / SlidePageDelegate / FadePageDelegate
│ ├─ ScrollPageDelegate / NoAnimPageDelegate
│ └─ SimulationPageDelegate(仿真卷曲——最难)
├─ AutoPager(自动翻页)
└─ 选择 / 光标 / TextActionMenu
分页排版(**不改,复用其输出**):
ChapterProvider / TextChapterLayout / ZhLayout / TextPageFactory
→ TextChapter / TextPage / TextLine / TextParagraph /
TextColumn·ImageColumn·ButtonColumn·ReviewColumn·TextHtmlColumn
关键文件(ui/book/read/page/):ReadView.kt、PageView.kt、
ContentTextView.kt、delegate/*.kt、entities/**、provider/**。
┌───────────── ReaderViewport(尺寸输入接缝)─────────────┐
│ widthPx / heightPx / density / contentPadding │
│ 旧 View 和 Compose 都能驱动同一个 ChapterProvider │
└──────────────────────────┬─────────────────────────────┘
▼
ReaderSession.state(Track A)──┐ ChapterProvider(排版引擎,不动)
ReadBook.snapshot ─────────────┤ │ 产出可变 TextPage/TextLine/*Column
选择/朗读高亮/动画进度/viewport ┘ ▼
ReaderPageSnapshot(不可变、renderer 专用)
│ 聚合进 ReaderRenderModel(StateFlow)
▼
Compose 阅读表面(Canvas + pointerInput)
- 分页结果 = 消费 snapshot 的只读几何,不直接读 TextPage
- 翻页动画 = Compose 本地动画状态(Animatable)
- 手势 = pointerInput
- 反转 upContentAwait 的 model→UI→model 往返
ReaderRenderModel 是 Track C 的地基:一个只读的渲染视图模型,聚合
A 的会话快照 + B 控制器持有的视图态(viewport 尺寸、动画进度、选择、朗读高亮、
预加载反馈)。它不持有可变领域对象,滚动高频字段与低频结构字段分流
(呼应 A4 的「高频 viewport / 低频结构」二流拆分——Track C 才真正需要它)。
两个新的地基构件(本版计划从「C2 脚注 / A4 预留」提升为独立阶段,理由见 §4):
ReaderViewport:把「谁给 ChapterProvider 定尺」从 ContentTextView.onSizeChanged
唯一入口,抽成一个可由旧 View 或 Compose 驱动的输入接缝。这不是重写分页引擎
——ChapterProvider 仍是唯一排版者,只是它的尺寸/重排版触发不再是 View 的私产。
跨过它,才可能「关掉底层正文 View,Compose 仍能独立分页」。ReaderPageSnapshot:当前 TextPage 是满是 var 的 data class(还内嵌
canvasRecorder 等可变绘制缓存),跨线程被 Compose 直接消费有陈旧/竞态风险。
快照是 renderer 专用的不可变投影(文本坐标 / baseline / 字体色 / 装饰 / 图片区 /
交互区 / 选择范围),第一版可为既有对象的只读视图,不要求深拷贝全部文本数据。本版排序修订(吸收一次外部审阅):原稿 C3 直接做「手势 + 翻页动画」,但当前 C2 是一层仍由旧
ReadView驱动分页的 overlay。在这层 overlay 上继续堆动画/图片/ 选择,会把「旧 View 定尺分页 + Compose 再画一遍」焊成永久双栈——每加一个功能都要 两边同步。故把「解除 View 尺寸依赖(ReaderViewport)」与「不可变ReaderPageSnapshot」 提前,作为一切复杂渲染之前的闸门;翻页动画与复杂交互整体顺延。本轨的核心里程碑 因此不是「Compose 又支持了一种绘制」,而是:关掉底层正文
ReadView后,Compose 仍能独立完成分页与静态阅读。跨过这个节点之后做图片/选择/动画,才是在建设新架构;否则只是在扩大双栈。 代价要如实记账:viewport 前置是「高风险、低可演示」的一步(见 §4),先啃它意味着在拿到 更多可视 parity 之前先处理最硬的接缝——这是有意的取舍,不是顺手。
定义一个显式的渲染者接口,让「旧 Canvas 渲染」与「Compose 渲染」藏在同一个边界 后面,flag 只切换实现、而非叠加:
interface ReaderRenderer {
fun render(snapshot: ReaderPageSnapshot) // 类型见 C5,C0 立边界时可先用占位
}
// LegacyRenderer(包 ContentTextView/delegate)、ComposeRenderer 各实现之
目的:堵住 ComposeReaderSurface 直接去戳 ReadBookController 各方法、慢慢又耦合
回来的路。有了这条边界,C7「Legacy 或 Compose 二选一」才是换实现,不是拆线。
诚实标注顺序:C1/C2 已落地时并未走这条边界(overlay 直连 store/controller)。
故 C0 是现在补的契约,落地时机并入 C3 稳定化——不是一个「更早做过」的阶段,而是把
既有 overlay 的隐性耦合收敛成接口。接口的 snapshot 化签名依赖 C5,C0 先立
「两个 renderer 一个边界、flag 切实现」这件事,参数类型到 C5 再定型。
验收:ComposeReaderSurface 经 ReaderRenderer 实现被调用,grep 证明它不再直接
引用 controller 的渲染方法;flag 关时旧路径逐字节不变。
ReaderRenderModel(无行为变化)——已落地(e3f935479)ReadBookController / ReaderSession 之上,把当前散落在 ReadView 里的
视图态(cur/prev/next TextPage、durPageIndex、viewport 尺寸、选择区间、
朗读高亮 span、动画进度、loading)收敛成一个 StateFlow<ReaderRenderModel>。ReadView 继续是唯一渲染者;C1 只是并行暴露状态,不接管绘制。LabSettings.composeRenderer(默认关,DataStore 持久化)。ComposeReaderSurface(Compose Canvas + drawIntoCanvas → native
drawText),从 ReaderRenderModel 渲染当前页:复用排版引擎已算好的坐标
(TextColumn.start / TextLine.lineBase)与 ChapterProvider.contentPaint/
titlePaint,文字位置/字号/字色与旧 ReadView 像素一致。三区点击翻页/呼出菜单。ReadBookController 收集 ReadBook.snapshot→publishSession,
渲染回调末尾 publishStructure 当前页,喂给 C1 的 ReaderRenderStateStore。实现取舍:overlay,而非「替换 ReadView」。 原稿设想 flag 开时用 Compose 表面 替换
ContentTextView。落地时核实到关键耦合:分页尺寸只由ContentTextView.onSizeChanged → ChapterProvider.upViewSize驱动,UP_CONFIG重排版事件也由 View 层消费。若直接抽掉ReadView,需把ChapterProvider定尺、 重排版触发、UP_CONFIG处理全部改由 Compose 侧驱动——风险大且当前无法真机核。 故 C2 采用叠加:旧ReadView仍充当引擎(分页/内容加载/换章/手势),ComposeReaderSurface不透明覆盖其上只负责画像素 + 点击翻页。这样 flag 关时 逐字节不变、可随时回退,且验证面最小。「真正替换ReadView」下沉到 C4(ReaderViewport接缝)——本版计划把它从这条脚注提升为独立的核心里程碑。
ImageColumn/ButtonColumn/ReviewColumn、下划线与背景色/背景图、双页、滚动模式。代码稳定化已落地:补齐
ReaderRenderer边界,结构状态改为在旧 View 处理完 渲染命令后发布,并修复pointerInput(Unit)的陈旧回调捕获。真机证据及采集脚本见track-c3-reader-parity-baseline.md。2026-07-21 真机结果(Samsung SM-S9310,《斗破苍穹》,20 页往返):稳定性检查通过—— Legacy / Compose 均回到同一正文页,无空白页、旧页闪回或章节错位。但静态 parity 未通过: Compose 缺旧页眉/页脚,正文颜色/高亮与旧 Canvas 不一致;全屏 MAE 25.77、差异像素 22.72%。 帧基线:Legacy 50/90/95/99 = 5/8/21/65ms(jank 4.13%),Compose = 30/42/44/53ms (jank 50.00%)。两路径当前动画语义不同(Legacy 有翻页动画,Compose 无动画直接换页),仅作 C7 对照基线,不直接比较优劣。
结论:已完成 C3 的「采集/冻结」工作,但未满足「普通文字页基本一致」的验收。继续之前先解决静态画面差异;不跨阶段进入 C4–C8。
2026-07-21 第二轮修正:补齐按列颜色/搜索强调/字体/字号/API 35 letter spacing 与
ContentTextView内容偏移后,单页全屏 MAE 降至 0.023、差异像素降至 0.113%,当前普通 文字测试页已基本一致。采集脚本也增加了 route 冷启动、ADB 超时、前进页变化断言和五轮往返; 完整五轮复采后,Legacy / Compose 均回到起点,终点全屏差异仅 0.0127%,未见空白页、旧页 闪回或章节错位。随后补采系统衬线字体 + 增大 3 级字号、明确的第 8→9 章边界、各 5 次 冷启动首个非空正文帧,以及 GPU overdraw;四项均已有证据,详见track-c3-reader-parity-baseline.md§8,C3 正式放行。
- 先不堆功能,把 C2 的 overlay 收口,为后续所有阶段建立可对比的地基:
- 旧渲染与 Compose 渲染的截图对比(普通文字页、换字体/字号、换章、快速翻页)。
- 清理遗漏的状态发布入口(
publishSession/publishStructure覆盖是否有漏发)。- 复核
@Stable使用、pointerInput回调捕获是否闭包了陈旧值。- 建立性能基线(首帧、翻页帧耗时、overdraw),供 C7 停止双绘后对照。
- 验收(真机):flag 开时,普通文字页与旧渲染基本一致,无明显空白页、旧页闪回、 章节错位。这一步只冻结现状,不推进架构。
ReaderViewport 接缝——让 Compose 尺寸能驱动分页(已落地,真机验收通过)2026-07-21 实施记录:已新增
ReaderViewport/ReaderPadding/ReaderLayoutMode与ReaderLayoutController,旧ContentTextView和 ComposesnapshotFlow测量统一经ReaderLayoutCoordinator驱动ChapterProvider;控制器按有效正文 尺寸去重,避免 View/Compose 对同一尺寸重复触发分页。UP_CONFIG(12)的重排版已改经该接缝,upContentAwait先等待 viewport 就绪,并新增layoutResultsrevision 流作为 C7 删除旧 suspend 往返的雏形。Kotlin 编译、debug assemble、新旧渲染状态与 viewport 接缝单测通过。2026-07-21 真机验收(Samsung SM-S9310):Legacy / Compose 各完成两轮、每轮 5 页 往返,均回到起点;普通页全屏差异像素 0.0127%,返回页差异 0%。Compose 模式从竖屏 5/9 重排为横屏 6/11,横屏连续翻至 10/11 后恢复竖屏落在对应内容的 8/9,未见空白、 旧页闪回、错章或崩溃。另用临时验收构建把 legacy surface 约束为
0×0,Compose 仍能 独立生成正文并翻页,确认分页路径不经过ContentTextViewlayout;采证后已撤销临时改动。 旧层完全不绘制时背景与页眉页脚缺失,属于 C6 静态阅读闭环的既知范围,不影响 C4 “Compose viewport 可独立驱动分页”的验收结论。C4 回归防护补强:
awaitViewport()改为最多等待 2 秒,未测量时返回null并继续旧 内容更新路径,避免后台加载 / 纯 TTS / View 未测量时无限挂起;单测覆盖无 viewport 超时。 Debug Compose 同时新增readerDetachLegacySurface=trueIntent 验收入口与tools/verify_reader_c4_detached.py,会断言 legacy surface 确实为0×0、Compose 首个正文帧 出现且点击后页面变化。正式 committed 路径已在 SM-S9310 复测通过,0×0 结论不再依赖 一次性临时构建。
把
ChapterProvider的定尺入口从ContentTextView.onSizeChanged → upViewSize唯一来源,抽成一个显式接缝:enum class ReaderLayoutMode { PAGED, SCROLL, DOUBLE_PAGE } data class ReaderViewport( val widthPx: Int, val heightPx: Int, val density: Float, val contentPadding: ReaderPadding, val mode: ReaderLayoutMode, // 预留:C6 双页/滚动会改布局,现在留位不实现 ) interface ReaderLayoutController { fun updateViewport(v: ReaderViewport) }旧
ContentTextView和 Compose 表面都调用它;UP_CONFIG重排版触发同样经此接缝, 不再是 View 层私产。红线:ChapterProvider仍是唯一排版者,这里只搬「谁定尺 / 谁触发重排版」,不重写分页内核。mode现在只有PAGED一条路径;留字段是为了 C6 的双页/滚动不必再引一个并列的ReaderModeController+ChapterProvider特判,避免接缝二次碎裂。
- 在本阶段后半段就开始拆
upContentAwait(不要留到 C7 才动):一旦 Compose 驱动 viewport,若upContentAwait仍等旧 View 的 layout 完成回调,就埋了一个「Compose 表面 看似独立、实际仍反向等 View 排版」的隐形旧架构。C4 后半把它改成LayoutResultFlow(排版完成信号)+snapshotFlow等测量完成的雏形;C7 只做最终删除。- 验收(真机):底层旧
ReadView可设为不可见、甚至不参与布局,Compose 仍能正确 生成页面;屏幕旋转 / 横竖屏 / 窗口尺寸变化能重新分页;且此路径不经过ContentTextView的 layout 回调。 注意:这一步不删ReadView,只解除它作为「分页生命线」的作用。跨过它,Compose 才从 overlay 变成能独立分页的渲染面。
ReaderPageSnapshot(renderer 只消费快照)——已落地,真机验收通过2026-07-21 实施记录:新增
ReaderPageSnapshot/ReaderElement及旧排版对象适配器, 在ReaderRenderStateStore.publishStructure边界把三页可变TextPage/TextLine/*Column投影为不可变绘制值;快照已包含文字坐标与完整画笔样式、装饰、图片区、交互区、选区和 revision,不携带排版逻辑、反向对象引用或 Canvas recorder。ComposeReaderSurface只消费 快照并重建私有TextPaint,已无ChapterProvider、旧页面实体或旧 View 绘制状态访问。 单测覆盖旧行列对象在发布后继续突变不会污染既有快照,重新发布才生成更高 revision。真机验收(Samsung SM-S9310):debug 构建安装后,以 legacy surface
0×0的 committed detach 路径启动 Compose 阅读器,首个非空正文帧 1611ms 出现;点击下一页后画面变化 17.76%,正文无空白、错位或崩溃。聚焦单测、compileAppDebugKotlin与installAppDebug均通过。
把旧引擎输出(可变
TextPage/TextLine/*Column)转换为 renderer 专用的不可变数据: 文本位置 / baseline / 字体色 / 装饰(下划线、背景)/ 图片区 / 交互区 / 选择范围。data class ReaderPageSnapshot( val chapterIndex: Int, val pageIndex: Int, val width: Int, val height: Int, val elements: ImmutableList<ReaderElement>, val revision: Long, ) sealed interface ReaderElement { data class Text(/* 坐标、文本、样式 */) : ReaderElement data class Image(/* 坐标、图片引用 */) : ReaderElement }第一版可为既有对象的只读视图,不要求深拷贝全部文本;但 renderer 不得再直接读
ChapterProvider或可变页面对象。呼应 A4 的「高频 viewport / 低频结构」二流拆分—— 滚动位置高频、结构快照低频,避免把 60fps tick 挂到 equality 快照。护栏:别让 snapshot 长成第二个
TextPage。 snapshot 只描述绘制(坐标/样式/ 区域),不承载排版逻辑、不回挂可变对象、不塞canvasRecorder这类缓存。保持「排版 描述」与「绘制描述」在概念上可分(未来若真需要 Web/View 多 renderer,可再把它拆成PageLayoutSnapshot → RenderSnapshot两层)。当前只有一个 Compose renderer,故不预先 引入两层类型(避免为未上路的多端渲染造抽象)——只在此立「不可反向依赖排版对象」的 纪律,让将来分层仍可能。验收:Compose renderer 只消费 snapshot,
grep证明其不再访问ChapterProvider/ 旧 View 的绘制状态。
在 snapshot 上把静态阅读功能补齐,全程不碰翻页动画:
ImageColumn / ButtonColumn / ReviewColumn 等列类型TextActionMenu(TextActionSelectionMenu 已是 Compose)mode=DOUBLE_PAGE)是否正确、prev/cur/next
> 页缓存是否合理,比长按选择/菜单更贴近架构核心。滚动仍垫底(真正的性能模式)。upContentAwait——暂缓(见顶部状态)upContentAwait(拆解已在 C4 后半开始):C4 已把它改成
LayoutResultFlow + snapshotFlow 等测量完成的雏形;C7 在切成单栈后做收尾——
删掉 ReaderRenderCallback.upContentAwait 这个 suspend 往返,退化为纯状态更新,
模型不再挂起协程反向等 View 排版。若 C4 后半未能完全脱开旧 View layout 回调,C7 必须
在这里补齐,不允许带着隐形往返翻默认值。翻页动画用 Compose 本地动画状态(Animatable/updateTransition),只消费 snapshot
与 transition state,不再深入改分页内核:
interface ReaderPageTransition {
fun draw(previous: ReaderPageSnapshot?, current: ReaderPageSnapshot,
next: ReaderPageSnapshot?, progress: Float)
}
逐个替换 PageDelegate 子类,从易到难:NoAnim → Cover → Slide → Fade →
Scroll → Simulation(仿真卷曲,最后做,涉及曲面/阴影,最高风险)。
每个 transition 迁完即在 flag 下与旧栈对比。放到最后的理由:动画同时牵涉当前/上/下页、 手势进度、页面缓存、中途取消、快速连击、章节边界、预加载、方向变化——页面数据与 viewport 未稳定前提前做,必然大量返工。
Simulation 兜底:仿真卷曲是唯一可能「Compose 做不出等价观感」的点;若代价过高, 可保留它为唯一的旧栈回退项,而非阻塞整轨。
LegacyReaderRenderController 承接的指令式渲染方法体、ReadView/PageView/
ContentTextView/delegate/*。ReaderRenderCallback 收敛为「发布渲染状态」
而非「下达渲染命令」。ReadView 后 Compose 仍能独立
分页 + 静态阅读」。在此之前,overlay 仍靠旧 View 定尺——任何在 overlay 上做的
图片/选择/动画都是双栈投资,早晚要在 C4 之后重做。这是本版把 viewport 解耦
(C4)与不可变快照(C5)提到动画前面的唯一理由。UP_CONFIG/重排版触发难以脱离 View,允许把 C4
拆成更小的子步,但不允许跳过它去做 C6/C8。ReaderRenderer 契约,ComposeReaderSurface 会慢慢
直连各 controller,C7 的「二选一」就退化成拆线而非换实现。C0 概念上最前,但因 C1/C2
已落地未走此边界,实际并入 C3 补齐。upContentAwait 拆解不等 C7:C4 让 Compose 驱动 viewport 的同时就要开始拆这个
suspend 往返,否则「Compose 看似独立、实则反向等旧 View layout 回调」的隐形旧架构会
一直潜伏到 C7 才暴露。C7 只做最终删除与兜底。ReaderRenderModel,后面每个 Compose 组件都会
各自去戳全局单例,变成「Compose 皮的分布式单体」。(C1 已落地。)ChapterProvider 一旦被顺手重写,风险与工期都失控。C4 只搬
「谁定尺 / 谁触发重排版」的接缝,不是造新的分页引擎——ReaderViewport 是输入
层,不是 ChapterProvider 的替代。ReaderRenderModel/ReaderPageSnapshot 必须
分流高频 viewport 与低频结构(A4 预留、C5 兑现),避免把滚动 tick 挂到 equality 快照上。Track C 完成前,upContentAwait/页面排版完成/翻页动画/选择取消/局部重绘/
page recorder 回收这些职责进渲染控制器/分页引擎,不进 ReaderSession 或 VM
(呼应主计划「ViewModel 拆分」的第三类)。C9 删旧栈后,这些职责随之消失或变为
渲染状态发布,VM 不再与渲染有任何关系。
立 ReaderRenderer 契约(C0,随 C3 补)→ 抽只读 ReaderRenderModel(C1,已落地)→
flag 下用 Compose 画静态页 overlay(C2,已落地)→ 稳住 overlay + 建 parity/性能基线
(C3)→ 抽 ReaderViewport(含 mode 预留)让 Compose 尺寸驱动分页、旧 ReadView 可关、
并开始拆 upContentAwait(C4,核心里程碑)→ 引入不可变 ReaderPageSnapshot(C5)→
无动画阅读闭环含图片/双页/点击/选择/滚动(C6)→ 停止双绘 + 删 upContentAwait(C7)→
翻页动画、仿真卷曲垫底(C8)→ 翻默认、删旧栈(C9)。
排版引擎全程不动,每阶段带 flag、可回滚、真机 parity。核心不是「Compose 又多画一种
东西」,而是先跨过「关掉旧正文 View、Compose 独立分页」这个节点,后面才是建设新
架构,而非扩大双栈。
注(2026-07-21):以上 C6–C9 描述的是「Compose 默认化 + 删旧栈」的完整路线,现已暂缓(见顶部状态)。当前落点是 C5:C0–C5 冻结为 flag 下可选渲染器,阅读渲染的近期主线转为 Track D(保留
ReadView、解其自身业务耦合,见mad-modernization-plan.md)。C6–C9 仅在将来专门的性能调查支持「Compose 不劣于旧 View」后才重启。