
                            
                            因收到Google相关通知，网站将会择期关闭。相关通知内容
                            
                            
                            40 动态化实践，如何选择适合自己的方案？
                            在专栏第36期《跨平台开发的现状与应用》中，我分享了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又使它重新焕发了青春。但是我们坚信，无论未来采用何种方案，都是为了给用户更好的体验，同时让业务可以更快地迭代，并在不断地尝试中，给用户提供更好的产品。

课后作业

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

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


2018年北京QCon：美团《美团客户端动态化实践》。

2018年GMTC：闲鱼《基于Google+Flutter的移动端跨平台应用实践》。

2018年GMTC：京东《当插件化遇上Android+P》。

2018年GMTC：小米《快应用开发与实现指南》。

2018年GMTC：绿色守护《Android+研发的昨天、今天与明天》。


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

                        
                        
                            