
很多团队现在都躲不开一个话题怎么把 Android 项目往 Jetpack Compose 上迁。我最早是被临时抓去评估这事儿的翻了几个月 issue、手动试迁了几个页面之后最直观的感受不是“能不能迁”而是“太烧心了”——大部分时间和精力都耗在重复无聊的翻译工作上。后来把 AI 辅助这套流程理顺了才总算把节奏拉回来。这篇文章就是把迁移的思路、AI 的实际用法、以及过程中踩过的坑完整整理出来给正在经历同样痛苦的朋友做个参考。1. 为什么说手写迁移 Compose 是场持久战1.1 迁移第三天就开始怀疑人生的典型场景任何一个上规模的安卓项目代码都不会是干干净净等着你迁的。我手里那个项目大概有十几个业务模块、二十多万行 Java/Kotlin 混编代码XML 布局 400 多个光是依赖的三方 UI 库就有七八个。第一周我挑了其中一个中等复杂的页面试着手写迁移三天下来整个人处于一种“我到底在干嘛”的状态。原因其实很真实。传统 View 体系里业务逻辑往往散落在 Activity、Fragment、Adapter、ViewHolder 里一个简单的列表页数据加载在主线程回调里点击跳转在 Adapter 里写死空态和错误态又由另一个状态类控制。迁移到 Compose 时不是简单地把 XML 翻译成 Composable 就行——你得把散在各处的逻辑重新组织成状态和数据流这本质上是架构重构不是翻译。第一个页面试完我对整个迁移周期的判断直接翻倍。1.2 “能跑”和“能交付”之间隔着大量脏活单纯要让一个页面在 Compose 下能跑起来说实话工作量没那么吓人。真正折磨人的是那些必须对齐的细节每个页面原来的间距、字号、颜色取值要跟设计稿和原实现完全一致深色模式下各种自定义背景和文字颜色的适配无障碍语义、焦点顺序、TalkBack 朗读内容列表滚动的性能特征比如 item 复用策略变化后会不会卡顿生命周期相关逻辑比如页面不可见时中断请求、销毁时取消任务。这些东西每一项都是体力活。手工做不是做不到而是会持续消耗团队的耐心。我当时的经验是一个平均复杂度的页面手工迁移加上单测、走查、兼容性验证差不多要 3 到 5 个人天。十几个页面排下来小半年就进去了而且这期间业务迭代还不能停两套 UI 体系同时维护需求一改就得改两遍。1.3 为什么 AI 恰好能切中这些痛点后来我开始尝试用 AI 辅助迁移第一感受是它并不是来替我做架构决策的架构判断还是靠人脑但 AI 把最耗时、最不需要创造力的“翻译型工作”提速了不止一个量级。举个例子把一个 200 行的 XML 布局转成等价的 Composable 代码手工敲一遍加调整状态一个下午是少不了的。用 AI 配合项目级上下文生成初稿十分钟内就能完成之后要做的只是审校和修正。更关键的是AI 在面对重复度高的代码模式时不会烦躁不会偷懒不会因为连续转换了五个列表页就漏掉第六个的空态处理。这种稳定性和耐心恰恰是手工迁移中最稀缺的。但要提醒一句AI 辅助迁移不是“输入命令就万事大吉”。如果项目复杂度高、自定义 View 多、业务状态纠缠严重AI 生成的东西经常编译都过不了。所以这篇文章后面会花不少篇幅讲“怎么把 AI 用得稳”而不只是“怎么让 AI 跑起来”。2. 迁移开工前的三件头等大事策略、清单与边界2.1 先盘家底再谈工具很多人一上来就把代码库整个丢给 AI让它“直接全部转成 Compose”。这种做法我试过效果非常差。大模型处理超大代码库时上下文有限而且没有明确边界时生成结果不可控改起来比从头写还头疼。正确的第一步是盘家底。我会建议做一个简单的评估表哪怕用 Excel 也行评估项具体内容对迁移策略的影响代码规模页面数、模块数、总代码行数决定分批拆分的粒度页面类型分布列表页、表单页、详情页、复杂交互页占比决定 Pilot 模块的选择自定义 View 情况是否有大量自定义绘制、手势处理决定哪些页面需要人工深度介入状态管理方案是否已引入 ViewModel/LiveData/Flow决定数据流改造的复杂度第三方 UI 依赖是否重度依赖某些老旧 UI 库决定是否需要先替换底层依赖设计规范完整度是否有统一的间距、色板、字体 token决定 AI 约束条件怎么写我那个项目盘完之后发现真正适合直接 AI 转换的页面只占 60% 左右剩下 40% 因为自定义 View 和复杂交互AI 只能做参考初稿核心逻辑还得自己写。这个判断如果开工之前不做后面一定会被现实教育。2.2 挑好切入点选对 Pilot 模块比快更重要迁移顺序是第二个容易翻车的地方。有一种观点是先挑最难的模块啃因为最难的部分搞定了其他模块都是降维打击。这种思路在某些重构场景下成立但在 Compose 迁移里我强烈不建议。原因在于 Compose 和传统 View 的编程模型差异太大团队需要一个“从陌生到顺手”的过程。如果第一个模块就是全项目最复杂的自定义画布加手势交互AI 生成的东西大概率惨不忍睹团队也会在挫败感中失去信心。我自己推荐的做法是挑一个中等复杂度、业务独立性高、但又不那么简单的模块做 Pilot。比如设置页、个人中心这种有列表、有表单、有状态切换足够暴露问题又不会因为太复杂让人崩溃。Pilot 模块的价值不是“迁完一个是一个”而是通过它把完整的流程跑通AI 怎么约束、校验怎么做、评审节奏怎么定、深色模式怎么验。这套方法论打磨顺了后面复制到其他模块才是真正的提速。2.3 把“迁移完成”定义清楚否则返工无从谈起还有一个很容易被忽略的问题你用什么标准判断一个页面“迁移完成”了。我在项目里踩过这个坑。最开始觉得“编译通过、页面能显示”就算完事结果视觉走查的时候发现一堆细节对不上某个按钮在深色模式下的按下态颜色变了、列表滑动到边缘的阴影效果消失了、自定义字体的行高被默认值覆盖了。这些问题的共性是它们不影响功能但影响体验而体验恰恰是迁移项目最容易返工的地方。后来我整理了一份“迁移完成检查清单”每个页面都必须过完这一遍才算完页面渲染结果与原版进行截图对比间距、字号、颜色逐项核对深色模式、浅色模式各跑一遍主流程确认无硬编码颜色残留无障碍焦点顺序与原版一致TalkBack 朗读内容完整横竖屏切换、系统字体缩放后布局不塌列表滚动帧率不能低于迁移前基线页面退出后协程和任务正确取消无内存泄漏。这份清单听着繁琐但实际操作中每次大概多花半天时间。比起整个页面合并进主分支后又被人指出“这不对那不对”再返工这半天的成本低太多了。3. 用 AI 批量迁移 Compose 的核心工作流拆解3.1 让 AI 先“读”懂项目再动手AI 辅助迁移最大的误区是把它当成一个“你问我答”的转换器。你丢一句“帮我把这个 XML 转成 Compose”它确实能给你一段代码但如果缺少项目级上下文这段代码大概率不符合你们项目的设计体系和架构约定。我现在的做法是在开始批量迁移之前把以下几类信息整理成一个项目级说明文档每次让 AI 处理具体文件时都把它带上项目的架构分层说明哪个是 data 层、哪个是 ui 层数据流怎么走设计规范摘要颜色 token 的命名、间距体系、字体分级代码风格约束用 Kotlin 的哪个版本、是否启用 compose compiler 的强模式、命名规范已知的不兼容点比如某个三方库还没有 Compose 版本遇到相关组件时不要直接照搬用法。这一步花的时间不多但对生成质量的影响非常大。同样的 XML 布局有了“用 Modifier 而不是 LayoutParams”“颜色必须取自主题而不是硬编码”“字符串资源统一走 stringResource”这三条约束之后AI 生成代码的可接受率会明显上升。实测下来给足上下文后 AI 生成初稿的返工率大概能下降四成左右。3.2 XML 布局转 Composable 的提示词模板具体到单个页面的转换我建议用结构化提示词而不是口语化的表达。这里放一个我实际用过的模板你可以直接复制修改你是一名资深 Android 工程师正在把一个 View 系统项目迁移到 Jetpack Compose。以下是项目级约束使用 Material3 组件库但按钮统一使用 app 内部封装的 AppButton颜色一律通过 LocalAppTheme.colors 读取禁止硬编码 Color(0x...)字符串统一使用 stringResource(R.string.xxx)禁止硬编码字符串dp 间距统一使用 theme.dimens 里的 token禁止直接写 Modifier.padding(12.dp)所有可点击区域必须设置 contentDescription。请把下面的 XML 布局转换为等价的 Composable 实现。要求保持原有布局结构和视觉层级所有状态提升到 Composable 参数不在内部直接创建 ViewModel列表部分使用 LazyColumnitem 类型明确区分处理加载、空态、错误三种状态不要只写正常态生成完整代码不要写伪代码。XML 内容如下...粘贴具体布局这个模板看起来啰嗦但每一句话都不是废话。明确禁止硬编码颜色、禁止直接创建 ViewModel、要求处理三种状态这些约束对应的都是实际迁移时最常出问题的点。AI 在约束不明确的情况下默认选择往往不是你想要的“生产级写法”而是“怎么简单怎么来”。3.3 动态逻辑迁移从“翻译”走向“状态建模”单个页面能显示了不代表迁移成功了。真正花时间的是动态逻辑原来写在 Activity 里的生命周期回调、写在 Adapter 里的点击事件、写在 Fragment 里的数据加载在 Compose 里都要重新组织成状态、事件和副作用。这一阶段我的做法是让 AI 先生成状态机骨架再由我来确认边界。提示词大概是这样以下是原有页面的事件流和状态定义页面打开时从 repository 加载用户信息loadingtrue加载成功写入 stateloadingfalse点击“编辑资料”跳转到编辑页等待返回结果后刷新页面不可见时如果有进行中的请求取消。请帮我设计对应的 Kotlin 状态数据类和 Composable 中的副作用调用方式。重点是状态类需要是不可变 data class副作用通过 LaunchedEffect 和 DisposableEffect 表达。这一步特别考察人的架构能力。AI 能给你一个规范的模板但“哪些状态应该提升到 ViewModel、哪些状态应该留在 Composable 内部、哪些副作用应该放 LaunchedEffect 还是 rememberCoroutineScope”这些问题还是得靠人来判断。我的经验是AI 写出的状态类直接可用的比例很高但副作用的位置经常需要调。尤其是协程作用域放错了位置会导致页面退出后任务还在跑这个是实际项目中非常容易踩的坑。3.4 跑通一次“最小批量迁移闭环”单独页面转换练熟了之后就可以开始批量推进。建议以模块为粒度而不是一次性铺开。我在项目里跑通了这样一条最小闭环列出该模块所有需要迁移的页面清单逐个页面用 AI 生成 Composable 初稿人工审校并修正状态管理、副作用、资源引用编译 单元测试跑通针对每个页面完成第 2.3 节里的“迁移完成检查清单”代码评审通过后合入主分支。整套流程走下来一个五个页面的模块大概需要一到两周。同样工作量放在手工状态下至少要翻三倍时间。而且因为每一轮都有完整校验合并后出线上问题的概率很低。我后来统计过整个迁移周期里大约七成代码是 AI 生成的但所有架构决策和最终质量把关都是人做的这个比例我觉得比较健康。4. AI 迁移里踩过的坑以及怎么让代码不出圈4.1 三种典型 AI 输出失误AI 不是万能的尤其是在没有明确约束的情况下。我总结了三种最常见、也最影响进度的失误类型。第一种是凭空生成不存在的 API。AI 可能为了让代码看起来更“现代”写了一个它自己觉得合理的函数但这个函数在你们项目的依赖版本里根本不存在。比如有一次它生成了一段调用animateContentSize()的代码而我们项目当时用的 Compose 版本里这个 API 的行为跟它理解的完全不同运行起来动画错乱。这种情况编译阶段不一定能发现因为 API 签名合法但语义不对只有运行时才能暴露。第二种是单文件看着完美跨文件状态管理一塌糊涂。AI 处理单个 Composable 时往往只盯着当前文件看不到全局的状态流向。最常见的问题是同一个页面里两个子 Composable 各自创建了自己的 ViewModel 实例导致状态不同步。这种错误在代码评审阶段很难一眼看出来因为每个文件单独看都是标准写法。第三种是风格不一致。如果没有明确约束AI 会在同一个项目里生成风格完全不同的代码有的地方用 Material 默认组件有的地方用项目封装的组件有的地方用 16.dp 间距有的地方用 12.dp。这种细碎的不一致后期统一成本特别高。4.2 Compose 编译器的“脾气”编译通过不等于没问题Compose 的编译器插件非常严格这是好事也是坏事。好的一面是很多低级错误在编译阶段就被拦住了坏的一面是有些问题它拦不住而且会让开发者产生“编译过了就安全了”的错觉。举一个实际发生的例子。AI 生成的一个列表页在编译和单测阶段都一切正常但上线后在低端设备上出现了明显的卡顿。排查后发现是 LazyColumn 的 item 类型没有拆分清楚导致所有列表项在滚动时都参与重组本来应该只有可见项更新结果整个列表都跟着刷新。这类问题本质上是状态粒度过大导致的编译器和单测都发现不了只能靠真机性能和运行日志来判断。所以我后来在审校 AI 生成代码时有一个习惯不只看它写了什么还要专门检查“状态被放在哪里、变化时会影响哪些重组范围”。如果一段代码里有很多个var state by remember或者mutableStateOf用得太发散我就会特别警惕宁可自己重构一部分也不直接放行。4.3 建立安全网模块验证、跨端走查、灰度放量AI 生成代码不可怕可怕的是没有约束机制就让这些代码直接进入主干。我在项目里建立了一套安全网虽然会增加一些流程成本但对比返工成本来说完全值得。每个模块迁移完成后至少要过三关。第一关是模块级自动化验证包括编译、单元测试、关键路径的 UI 自动化冒烟。第二关是横向走查由不参与该模块迁移的同事对照原版页面从视觉和交互上找差异走查重点包括深色模式、无障碍、横竖屏切换。第三关是灰度放量先在内部体验版跑两天收集崩溃和 ANR 日志确认没有明显问题后再进入正式版本的灰度渠道。这套安全网下来我负责的迁移项目在正式上线后几乎没有因为迁移本身导致线上事故。印象最深的一次是模块走查时同事发现某个页面的 TalkBack 焦点顺序与原版不一致如果当时只靠自动化和 AI 验证这个问题大概率会被漏掉。5. 迁移不是终点落地后的校验与指标验证5.1 迁移后真正要盯的指标不是包体积很多人迁移完成后第一件事是看包体积降了多少这当然值得看但我觉得更重要的几个指标是冷启动时间、页面帧率、崩溃率和内存泄漏情况。以我的经验Compose 迁移后的包体积通常会下降一些因为不再需要维护两套 UI 框架的运行时依赖但这不是迁移的核心价值。真正的收益是开发效率的提升一个新页面从原型到上线的时间相比传统 View 模式明显缩短迭代功能时不再需要改动一堆样板代码。而风险主要集中在运行时性能上。迁移后的页面如果出现掉帧第一优先级要排查重组范围是否过大第二优先级是 LazyColumn 的 key 和 item 类型是否合理第三优先级是是否存在不必要的状态读写。我在迁移后期专门留了一周时间做性能调优重点就是处理这类问题。5.2 用户视角的回归测试可以自动化到什么程度回到测试。手工回归的成本很高所以能自动化的部分尽量自动化。我的做法是把回归测试分成三层每一层都有明确的自动化和人工边界。第一层是编译期和单元测试层覆盖纯逻辑比如状态类转换、数据格式化、业务规则判断这一层完全自动化每次提交都跑。第二层是组件冒烟层用 Robolectric 或者 Compose UI testing 覆盖每个页面在加载、空态、错误态下的基础渲染这一层可以自动化到 80%。第三层是视觉走查层涉及深色模式、字体缩放、无障碍行为、真机性能这一层自动化能覆盖一半另一半必须靠人工。这套体系最大的好处是每次 AI 批量生成一批新页面后至少能保证第一、第二层不亮红灯第三层的问题再通过人工走查来兜底。如果你团队的人手紧张我建议至少把第二层做扎实因为这一层是兜住“迁移后页面直接崩在用户面前”的最后防线。5.3 迁移完成后还能干什么释放 Compose 的红利最后说一点迁移完成后的扩展方向。很多团队迁到一半的时候会觉得“终于能喘口气”但我建议在完成核心页面迁移后留出一点精力去尝试 Compose 独有的能力比如动态主题切换、声明式动画、按需加载内容组合。这些能力在传统 View 体系下实现成本很高迁移完成后的代码库反而有天然优势。我在项目里挑了一个非核心但曝光量高的页面用 Compose 的动态主题做了个沉浸式节日主题换肤整个开发周期只用了三天效果和性能反馈都不错。放在以前 View 体系的主题方案下这个需求怎么也要排一个月。这就是迁移完成后的红利不是“终于做完了”而是“接下来可以做得更多”。我在实际项目中最大的体悟是AI 在迁移中的角色更像一个不知疲倦的翻译官它会给你铺好大部分砖但路线怎么走、哪里要架桥、哪里要绕行还是得自己拍板。团队里配置一位对 Compose 状态管理和性能模型有感觉的工程师来把关 AI 输出比单纯追求“AI 生成率”——把多少行代码交给 AI 来写——更有意义。迁移这件事真正让人不烧心的不是代码一次就能跑通而是你清楚每一个决策背后的理由也清楚出了问题该去哪里排查。