Jetpack Compose到底值不值得迁移?我踩了这些坑之后终于有了答案

🔑 关键词:Jetpack Compose, View系统, 迁移陷阱, 性能对比, 安卓开发

📖 摘要:一个在原生View里苟了五年的人,花了一周把项目迁到Compose,又花了一个月收拾烂摊子。这里有真实数据、崩溃记录和最后悔的决定。

先说背景。我在公司维护一个下载量快破千万的老项目,UI层全是用View+XML堆的,Fragment里到处是findViewById和匿名内部类。去年年底老板突然说要做新版本,视觉稿全是最新Material样式,我盯着那些复杂动画和动态列表,越想越绝望——用View写这个,怕不是要加一个月的班。于是我心一横,拍板把新模块迁到Jetpack Compose。当时我天真地以为,官方吹了这么久,总有它的道理。

图片

第一周上手确实爽,写UI直接用函数,不玩布局嵌套,状态驱动也符合直觉。但真正的对比要等我把两个模块都做完。我拿同一台小米11做测试,同样的订单列表页,View版本构建时间是9秒,Compose版本点击编译后是14秒,而且第一次完整构建慢到接近40秒——每次改个按钮颜色都得等上十秒,我无数次怀疑自己是不是在开发安卓。运行时性能也没有想象中美好,我用Perfetto抓了帧数据,Compose列表在快速滚动时有轻微掉帧,平均帧时间比View版本多了1.2ms,虽然肉眼不仔细看看不出来,但那种不安全感是真实的。另外APK体积,仅仅一个模块就大了3.8MB,对小程序分包用户简直是噩梦。

图片

最让我崩溃的是第三方库的兼容性问题。我们项目里有一个老牌自定义Behavior的滑动隐藏逻辑,View时代直接写在CoordinatorLayout里,三十年没出过事。到了Compose,我找了半天发现官方只给了一个实验性的Modifier.nestedScroll,参数回调跟我想的完全不同。我参考网上各种帖子和源码,折腾了两个晚上终于模拟出了相似效果,但那个触摸事件的分发顺序还是有bug,用户快速上下滑动时会闪现一段空白。后来我实在没办法,只好用AndroidView包一个RecyclerView塞进Compose页面里——那一刻我突然觉得,自己像是为了省一块钱打车费,结果转了两趟公交还迷路了。

图片

另一个大坑是状态管理。网上吹StateFlow+collectAsState怎么优雅,我用了之后发现,只要你的页面里有多个异步任务,状态一多,重组频率高得吓人。我有个搜索页面,输入文字时会直接触发网络请求,我忘了做debounce,结果每次键盘跳动UI就闪一下,日志里显示一秒钟重组了20多次。后来我引入了derivedStateOf和rememberSaveable这类工具,才勉强压住。但这让我越来越怀疑,Compose的所谓"声明式"并没有减少复杂度,它只是把复杂度从xml和回调转移到了状态和重组里。你要是没想明白整个数据流,写出来的代码比屎山还臭。最后我说个结论吧:如果你团队里全是Compose新手,项目都是内部小工具或者原型演示,那直接上没毛病。但要是像我们这种老项目,牵一发动全身,我真心建议一步一步来。先把非核心页面用Compose试水,编好了再慢慢扩大边界。同时一定要盯紧APK体积和构建时间,最好搭个专门的性能监控。我折腾完这一个月,最大的感受就是:技术选型别跟风,得看自己家底。反正下次谁再跟我说"一行就能写出动画",我直接把他拉去维护我的搜索页面去。

图片