真当Flutter不能热更新?众能动态化Flutter
效果展示
上面铁军中的这个关于我们页面以及转换为了动态化页面。
动态化标签
动态化路由
一个flutter页面转换为动态化页面,只需要添加动态化标签和动态化路由就行了,对业务页面的开发修改很少,相比其他动态化方案是巨大的优势。
方案对比
方案名称 | 美团 | 满帮 | 众能 |
---|---|---|---|
简述 | dart文件编译为json,使用自定义运行时调用proxy方法 | 使用js通过自定义js虚拟机调用flutter业务组件 | dart文件编译为lua字节码,使用lua虚拟机调用proxy方法 |
开发语言 | dart | js | dart |
语法支持 | 不支持async和await | 支持async和await | |
flutter控件支持 | 全部支持 | 只能使用自定义组件 | 全部支持 |
开发调试 | 使用dart自带debugger | 自定义debugger | 使用dart自带debugger |
核心原理 | 使用自定义运行时解析json,调用proxy | 通过自定义js虚拟机调用proxy | 使用修改后的lua虚拟机调用proxy方法 |
三个方案都必须使用proxy方法,因为flutter中的dart阉割了反射,必须通过某种方式通过proxy方法来调用dart方法。
proxy方法如下图,这是扫描铁军App自动生成的:
众能的方案对比美团,优势在于使用了成熟的lua虚拟机的机制,在运行时执行的是指令,例如加法
会被编译两个loadConstant指令和一个binaryArith指令。而美团的方案核心是自定义的运行时,基本单位是语句和表达式,粒度粗了一个级别,直接的影响就是不支持async和await等异步语法。
众能的方案对比满帮,优势在于自动支持所有的flutter组件、第三方和自定义组件,同时开发调试时是使用flutter自带的编译器和调试器。而满帮方案开发时只能使用js语言,同时只能使用有限的flutter组件,有限的业务组件。
核心原理比喻
众能的方案核心其实和乐高积木一样:
普通flutter页面就像上面这个塑料恐龙,要改动一点必须在工厂重新生产。
使用众能动态化的页面,相当于使用很多个小块的乐高积木模仿出了原始的页面,小块的乐高积木就是proxy方法。
所以动态化页面其实是高仿的页面,和原始的页面有些细微的不一致。
但是优势就是动态化的页面可以在自己在运行时修改。
动态化流程1-jenkins打包
jenkins流水线在xcode编译前加入了动态化工具下载,以及动态化编译就是流程图中黄色的部分。
动态化流程2-启动注册路由
每一个动态化dart文件都会编译出.out字节码文件,但只有带routerModel的页面.out文件都才要在启动时执行生成routerModel
.out字节码文件就是上图这样,在启动时执行这个字节码就会生成routerModel,然后动态化虚拟机就休眠了,直到进入动态化页面才会启动。
这个字节码的内容是和lua字节码完全一致的,甚至在项目初期真正的lua字节码可以在mars_vm中运行并输出一样的结果,python、java等虚拟机中字节码也是类似结构,都有指令集,本地变量集合等信息。 虚拟机是另外一种语言来执行目标语言的,例如lua官方虚拟机是使用c语言来执行lua,也有go语言版本的。python虚拟机有java、c、go等多个版本。 众能的这个虚拟机是dart实现的,可以执行lua和dart。
动态化流程3-进入页面
进入页面后就会执行路由routerModel的block,完成页面初始化为为statefullWidget,并按照StatefullWidget本身的生命周期调用对应方法。
最简单的虚拟机指令展示
动态化运行hello_world代码,完成hello world字符串打印,这个在虚拟机里面是比较复杂的,因为要用到包加载等功能。 最简单的指令是算数运算指令,例如下面这个加法。
a = b + 100
会被编译为三条指令,load指令从寄存器中加载b到栈顶,load指令从常量表中加载c到栈顶, 执行算数指令把栈顶的两个数弹出进行算数运算,结果放入a寄存器的位置。
待完善的功能点
1、目前虚拟机调试时不能和原始的dart代码对应上,还好预留了行号信息等字段,一个月内应该可以加上。
2、目前的命名空间实现不够完善,同一个库内类名不能重复。
3、调用proxy方法的指令过多,通过修改命名空间的机制,应该可以把指令数减半。
4、没有实现方法缓存,反复调用同一个方法的话,会重复使用指令加载这个方法。
转载自:https://juejin.cn/post/6911590460800647175