前段时间我写过一篇文章:Flutter官方正在搞热更新(动态化)?硬核,干货,有证据,有代码
介绍了并验证Flutter官方动态化框架dynamic_module的可行性。
今天刚好有时间分析一下dynamic_module的运行原理。直接说结论吧(说太多分析过程我想大家也不喜欢看):在实现上dynamic_module还是比不上shorebird的。
dynamic_module目前有几个比较大的缺点:
- 需要通过配置去定义母程序里可扩展的部分,加大了复杂度。
- 动态化的粒度是以包为单位的,没有办法做到只覆盖一个方法或一个类。
- 假设动态化的范围是 dart:core以及pacakge:flutter下面的所有包,经过配置编译出的母包会大30M左右,如果还有额外的第三方包,可能会更大。
从运行原理上dynamic_module和shorebird有很大不同:
dynamic_module的母包在编译时需要dynamic_interface.yaml来定义母包可扩展范围:
# dynamic_interface.yaml
extendable:
- library: 'dart:core'
- library: 'package:flutter/*'
- library: 'package:demo_dynamic_feature_modules/*'
can-be-overridden:
- library: 'dart:core'
- library: 'package:flutter/*'
- library: 'package:demo_dynamic_feature_modules/*'
callable:
- library: 'dart:core'
- library: 'package:flutter/*'
- library: 'package:demo_dynamic_feature_modules/*'
我们都知道原本可能会在编译成aot代码时通过TreeShaker机制将未被调用过的方法或者类都去掉,但是经过callable标记的AST节点不会走TreeShaker机制,这就是为什么母包配制dynamic_module后会增大30M左右的最大原因。因为上面的配置里'dart:core'和'package:flutter/*'的所有代码都原封不动的保留在代码里了。
而反观shorebird并没有这方面的问题,shorebird的母包并不会增加原本的大小(或者加的比较少),且shorebird支持方法层级的修复。甚至在Android上,实际上是做可执行产物差异化的对比和增量更新,性能上并没有任何损耗。
当然 dynamic_module也并不是没有优点,dynamic_module能在引擎启动后任意时间选择是否加载某个补丁。而shorebird只能在引擎启动时就去做这些。
因此如果你的需求是模块化,或者是想做一个类似小程序的平台那么shorebird就不太合适,但dynamic_module就能做到这一点。
dynamic_module的另一个优点就是它是支持wasm的,当然这个我们是很少用到就是了。
我个人感觉dynamic_module还有改进的空间,比如保留TreeShaker机制,如果补丁中有用到被TreeShaker去掉的某些类或方法,可以将这些去掉的部分添加到补丁里,虽然补丁变大了,但补丁仍然能够正常运行。当然,实际处理起来是比较复杂的。
写到这里我不得不感叹shorebird还是牛的,之前的断言草率了。