You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

18 KiB

40 | 动态化实践,如何选择适合自己的方案?

在专栏第36期[《跨平台开发的现状与应用》](https://time.geekbang.org/column/article/88161)中我分享了H5、React Native/Weex、小程序这几种常见的跨平台开发方式。站在开发的角度虽然跨平台的开发效率要比Native开发更高但是这并不是大前端在国内盛行的最主要原因。

相比跨平台能力,国内对大前端的动态化能力更加偏执。这是为什么呢?移动互联网已经发展十年了,随着业务成熟和功能的相对稳定,整体重心开始偏向运营,强烈的运营需求对客户端架构和发布模式都提出了更高的要求。如果每个修改都需要经历开发、上线、版本覆盖等漫长的过程,根本无法达到快速响应的要求。

所以H5、React Native/Weex、小程序在国内的流行可以说是动态化能力远比跨平台能力重要。那我们应该选择哪一种动态化方式呢正如我在跨平台开发所说的目前这几种方案或多或少都还存在一些性能问题如果一定要使用Native开发方式又有哪些动态化方案今天我们一起来学习应该如何选择适合自己的动态化方案。

动态化实践的背景

前几天在朋友圈看到说淘宝iOS客户端上一个版本的更新已经是两个多月前的事情了。淘宝作为一个业务异常庞大且复杂的电商平台这样的发布节奏在过去是很难想象的。

而现在即使不通过发布新版本,我们也能实现各式各样的运营活动和个性化推荐。依赖客户端的动态化能力,我们不需要等待应用商店审核,也无须依赖用户的主动更新,产品在快速迭代的同时,也有着非常强大的试错能力。

1. 常见的动态化方案

移动端动态化方案在最近几年一直是大家关注的重点,虽然它已经发展了很多年,但是每年都会有新的变化,这里我们先来看看各大公司有哪些已知的动态化方案。

在2018年北京QCon大会上美团工程师分享了他们在动态化的实践《美团客户端动态化实践》。美团作为一个强运营的应用,对动态化有非常强烈的诉求,也有着非常丰富的实践经验,他们将动态化方案分为下面四种类型。

  • Web容器增强。基于H5实现但是还有离线包等各种优化手段加持代表方案有PWA、腾讯的VasSonic、淘宝的zCache以及大部分的小程序方案。

  • 虚拟运行环境。使用独立的虚拟机运行但最终使用原生控件渲染代表方案有React Native、Weex、快应用等。

  • 业务插件化。基于Native的组件化开发这种方式在淘宝、支付宝、美团、滴滴、360等航母应用上十分常见。代表方案有阿里的Atlas、360的RePlugin、滴滴的VirtualAPK等。除此之外我认为各个热修复框架应该也属于业务插件化的一种类型例如微信的Tinker、美团的Robust、阿里的AndFix。

  • 布局动态化。插件化或者热修复虽然可以做到页面布局和数据的动态修改但是代价巨大而且也不容易实现个性化运营。为了实现“千人千面”淘宝和美团的首页结构都可以通过动态配置更新。代表的方案有阿里的Tangram、Facebook的Yoga。

2. 动态化方案的选择

四大动态化方案哪家强,我们又应该如何选择?在回答这个问题之前,我们先来看看它们的差别。

目前我们还无法找到一种“十全十美”的动态化方案每种方案都有自己的优缺点和对应的使用场景。比如Web容器增强方案在动态化能力、开发效率上有着非常大的优势但稳定性和流畅度差强人意。恰恰相反布局动态化方案在性能上面有非常不错的表现但是在动态化能力和开发效率上面却受到不少限制。

所以说动态方案的选择,我们需要考虑下面这些因素。

  • 业务类型。主要考虑业务的重要性、交互是否复杂、对性能的要求、是否长期迭代等因素。

  • 团队技术栈和代码的历史包袱。在选择方案的时候也需要结合团队的技术栈现状以及代码的历史包袱综合考虑。以微信为例作为一个强交互的IM应用团队基本以Native开发为主而且微信基本没有太多运营上的需求所以当时在动态化方案上只使用了Tinker。当然团队的技术栈并不是永恒不变有了微信小程序之后内部的一些业务也尝试使用小程序来改造。

最终无论我们选择哪种动态化类型我都建议公司内部同一种动态化类型都使用同一个方案这样在统一技术栈的同时也可以实现代码在不同业务之间的迁移。比如阿里内部的虚拟运行环境统一使用Weex一个业务在手淘的效果不错也可以快速迁移到飞猪、天猫等其他应用中实现应用的流量矩阵。

同样对于运营活动也是如此,阿里内部有一个叫PopLayer的组件它可以在任意Native页面这个页面甚至可以是Browser弹出H5的部署容器可以在无需发版的情况下对已有的Native界面上浮出透明浮层并且可以不影响Native页面本身的交互。这样做活动我们不需要在客户端提前一两个月开发代码而且同一个活动也可以快速在公司内部的各个应用中上线。

Native动态化方案

Web容器增强和虚拟运行环境方案通过独立的Runtime和JS-SDK来桥接Native模块而业务插件化则通过插件化框架和接口能力直接调用。相比之下前者更加抽象而不易造成代码混乱这也是目前各大公司逐渐开始“去插件化”的原因。

最近两年大前端开发越演越烈传统的Native动态化方案是否还存在价值它又该何去何从热修复、插件化这些方案的未来又将如何演进呢

1. 热修复和插件化

2016年在开源Tinker的时候有两件事情是超出我预料的一个是热修复在国内竟然有那么大的反响另外一个就是它竟然如此的“坑坑不息”。

《Tinker技术的初心与坚持》一文中你可以看到过去我们踩过的一小部分坑但非常不幸的是填坑之路至今依然没有结束。每次Android新版本发布我们就像迎来期末考试一样步步惊心。

曾经微信希望使用Tinker来代替版本发布在热修复的基础上实现四大组件的代理。但是Android P私有API限制的出现基本打消了这个念头。热修复不能代替版本发布但是我们可以通过它来实现一些应用商店不支持的功能例如精准的灰度人数控制、渠道和用户属性选择、整包的A/B测试等。

另一方面热修复给国内的Android生态也带来一些不太好的影响比如增加用户ROM体积占用、App启动变慢15%、OTA首次卡顿等。特别是Android Q之后动态加载的Dex都只使用解释模式执行会加剧对启动性能的影响。因为性能的问题目前大公司基本暂停了全量用户的热修复只使用热修复用于灰度和测试。

热修复如此,插件化也是如此。笨重的插件化框架不仅影响应用的启动速度,而且多团队协作的时候并没有想象得那么和谐,接口混乱、仓库不好管理、编译速度慢这些问题都会存在。插件化回归模块化和组件化,这也是目前各大公司都在逐步推进的事情。

前一阵子,徐川在《移动开发的罗曼蒂克消亡史 》一文中回顾了热修复和插件化的前世今生。时间一转三年过去了,对于曾经参与这个浪潮的一份子来说,我可以做的只是顺应潮流的变化。

热修复的未来

Tinker设计之初参考了Instant Run的编译方案但是正如专栏第26期《关于编译,你需要了解什么?》中所说的Google在Android Studio 3.5之后对于Android 8.0以上的设备将会使用Apply Changes替代之前的Instant Run方案。

Apply Changes不再使用插入PathClassloader的方式而是使用我们已经多次讨论过的JVM TI。在Android 8.0之后JVM TI开始逐渐支持ClassTransform和ClassRedefine这两个接口它们可以允许虚拟机在运行时动态修改类实现运行时的动态字节码编织。事实上这个技术在JVM就已经非常成熟Java服务端利用这两个接口实现了类似热部署、远程调试、动态追踪等能力具体你可以参考《Java动态追踪技术探究》

那热修复的未来将要走向何方本来我对热修复的未来是非常悲观的但是Android Q给了我一个很大的惊喜。我们知道Android P在中国有非常多的应用出现了兼容性问题其中大部分是热修复、插件化以及加固等原因造成的Google提供的数据是43%的兼容性问题由这三个问题造成)。

为了解决这个问题并且减少我们对私有API的调用Google在Android P新增了AppComponentFactory API并且在Android Q增加了替换Classloader的接口instantiateClassloader。在Android Q以后我们可以实现在运行时替换已经存在ClassLoader和四大组件。中国热修复的先驱们用自己的“牺牲”总算换来了Google官方的支持。我们使用Google官方API就可以实现热修复这样以后Android版本再升级也不用担惊受怕了。移动开发的罗曼蒂克并没有消亡Native的热修复再次迎来了春天。

插件化的未来

对于插件化的未来我们需要思考如何“回归官道”。Google在2018年推出了Android App Bundles,它可以实现模块的动态下载,但是与插件化不同的是,它并不支持四大组件代理的能力。

但是Android App Bundles方案依赖Play Service在国内我们根本无法使用。爱奇艺的Qigsaw可能对我们有所启发它基于Android App Bundles实现支持动态更新但是不支持四大组件代理同时完全仿照AAB提供的Play Core Library接口加载插件如果有国际化需求的公司可以在国内版和国际版上无缝切换。这种方案不仅可以使用Google提供的编译工具链也支持国际国内双轨相当于Google为我们维护整个组件化框架在国内只需要实现自己的“Play Service”即可。

当然和热修复一样,如果使用AppComponentFactory API我们也可以实现插件化的四大组件代理。但是具体实现上依然需要在AndroidManifest中预先注册四大组件然后具体的替换规则可以在我们自定义的AppComponentFactory实现类中埋好。

以Activity替换为例我们可以将某些类名的Activity替换成其他的Activity新的Activity可以在补丁中也可以在其他插件中。

热修复和插件化作为Native动态化方案它们有一定的局限性。随着移动技术的发展部分功能可能会被替换成小程序等其他动态化方案。但是从目前来看它们依然有非常大的存在价值和使用场景。

2. 布局动态化

正如上文所说像淘宝、美团首页这些场景我们对性能要求非常高这里只能使用Native实现。但是首页也是流量的聚集地“提增长、提留存、提转化”都要求我们有强大的运营能力。最近两年淘宝、天猫一直推行“千人千面”每个用户看到的主页布局、内容可能都不太一样。

布局动态化正是在这个背景之下应运而生,在我看来,布局动态化需要具备下面三个能力。

  • UI容器化。能够动态地新增、调整UI界面而无需发版。

  • 能力接口化。点击、跳转等通用能力可以通过路由协议对外提供满足UI容器化后的调用需求。

  • 数据通道化。数据上报也可以通过字段配置,实现客户端根据配置自动上报。

在具体的实践上,天猫开源的Tangram是一个不错的选择。但是Tangram的整体方案会相对复杂一些我们也可以基于底层的VirtualView做二次开发。

总的来说布局动态化相比虚拟运行环境来说它不仅实现了UI的动态新增和修改也有着良好的体验和性能同时接入和学习成本也比较低。

总结

“路漫漫其修远兮吾将上下而求索”我们对动态化实践的探索一直没有停止。今年Flutter也强势地杀入了这个“战场”那Flutter在跨平台和动态化方面表现如何我们将在专栏下一期中揭晓。

动态化如今在国内是炙手可热的研究方向,虽然每个公司都强行造了自己的轮子,但是动态化方案目前还有很多没有解决的问题。所以在我们解决这些问题的过程中,也还会不断演变出其他的各种新方案。

现在各种类型的动态化方案目前都能找到自己的应用场景。移动技术在快速地发展我们无法准确预料到未来比如说在Android P我们正准备放弃热修复的时候Android Q又使它重新焕发了青春。但是我们坚信无论未来采用何种方案都是为了给用户更好的体验同时让业务可以更快地迭代并在不断地尝试中给用户提供更好的产品。

课后作业

对于动态化实践,你有什么看法?在你的应用中,使用了哪种动态化方式?欢迎留言跟我和其他同学一起讨论。

跨平台和动态化可以说是大前端时代最大的两个特点,也是每年技术大会的重点。今天的课后作业是仔细阅读下面大会的分享内容,学习各大公司在大前端的实践经验,并留言写写你的心得体会。

欢迎你点击“请朋友读”,把今天的内容分享给好友,邀请他一起学习。我也为认真思考、积极分享的同学准备了丰厚的“学习加油礼包”,期待与你一起切磋进步哦。