多是目前放出来的相对最完整的Flutter动态化方案
跨平台新明星Flutter推出后,咱们团队也尝试引入 Flutter ,作为iOS开发,立刻感觉到,Flutter 虽然强大,但不能像RN同样动态化是阻碍咱们使用她的惟一障碍了。看Google团队对动态化的计划,短时间内应该不会上线,因此本身动手,启动了这个技术探索项目。git
项目代号:MXFlutter (Matrix Flutter)核心思路是把 Flutter 的渲染逻辑中的三棵树中的第一棵,放到 JavaScript 中生成。用 JavaScript 完整实现了 Flutter 控件层封装,可使用 JavaScript,用极其相似 Dart 的开发方式,开发Flutter应用,利用JavaScript版的轻量级Flutter Runtime,生成UI描述,传递给Dart层的UI引擎,UI引擎把UI描述生产真正的 Flutter 控件。因此在iOS上是彻底动态化的 ,预览版的完整代码在github:若是能帮助到你们,请给MXFlutter点个Star,给咱们有动力继续更新下去^_*,也让整个Flutter社区都能了解到咱们中国开发者的贡献。github TGIF-iMatrix MXFlutter继续前先瞥一眼总体的架构,一句话介绍MXFlutter,就是用JavaScript,以Flutter的写法开发Flutter。汗…仍是有点绕,你们看下面贴出来的代码吧。github
如下截图是在MXFlutter框架下用JS开发,你们能够把上面的源码下载下来,里面有完整的JS代码示例:web
下面是UI截图对应的JS代码,没错,你没有眼花,这个真的是 JavaScript 代码,能够在 MXFlutter 的运行时库上渲染出 Flutter 的UI面试
class JSPestoPage extends MXJSWidget { constructor() { super("JSPestoPage"); this.recipes = recipeList; } build(context) { let statusBarHeight = 24; let mq = MediaQuery.of(context); if (mq) { statusBarHeight = mq.padding.top } let w = new Scaffold({ appBar: new AppBar({ title: new Text("Pesto Demo") }), floatingActionButton: new FloatingActionButton({ child: new Icon(new IconData(0xe3c9)), onPressed: this.createCallbackID(function () { }), }), body: new CustomScrollView({ semanticChildCount: this.recipes.length, slivers: [ //this.buildAppBar(context, statusBarHeight), this.buildBody(context, statusBarHeight), ], }), //body:this.buildItems()[0] }); return w; } buildAppBar(context, statusBarHeight) { return SliverAppBar({ pinned: true, expandedHeight: _kAppBarHeight, actions: [ IconButton({ icon: new Icon(new IconData(1)), tooltip: 'Search', onPressed: this.createCallbackID(function () { }), }), ], flexibleSpace: LayoutBuilder({ builder: function (context, constraints) { size = constraints.biggest; appBarHeight = size.height - statusBarHeight; t = (appBarHeight - kToolbarHeight) / (_kAppBarHeight - kToolbarHeight); extraPadding = new Tween({ begin: 10.0, end: 24.0 }).transform(t); logoHeight = appBarHeight - 1.5 * extraPadding; return Padding({ padding: EdgeInsets.only({ top: statusBarHeight + 0.5 * extraPadding, bottom: extraPadding, }), child: Center({ child: new Icon(new IconData(1)) }), }); }, }), }); } buildBody(context, statusBarHeight) { let mediaPadding = EdgeInsets.all(0); let mq = MediaQuery.of(context); if (mq) { mediaPadding = MediaQuery.of(context).padding; } let padding = EdgeInsets.only({ top: 8.0, left: 8.0 + mediaPadding.left, right: 8.0 + mediaPadding.right, bottom: 8.0 }); return new SliverPadding({ padding: padding, sliver: new SliverGrid({ gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent({ maxCrossAxisExtent: _kRecipePageMaxWidth, crossAxisSpacing: 8.0, mainAxisSpacing: 8.0, }), delegate: new SliverChildBuilderDelegate( function (context, index) { let recipe = this.recipes[index]; let w = new RecipeCard({ recipe: recipe, onTap: function () { showRecipePage(context, recipe); }, }); return w; }, { childCount: this.recipes.length, }), }), }); }
源码中还有更丰满的示例,高仿知乎页面JSFlutter版 github.com/TGIF-iMatri… ,这是对应UI,已经接近在线上版直接使用了。shell
这个漂亮的知乎页面,是用Dart版转JS而来,在此鸣谢做者许吉友 ,你们能够关注一下他。设计模式
MXFlutter虽然各个模块已相对完整,但投入生产还须要解决其中的BUG,因为19年初,小组启动新项目,很是繁忙,几乎没有时间继续开发,从3月份一直暂停,目前人力仍然很紧张,因此先放出技术方案和预览版代码,若是你们有兴趣,期待小伙伴们一块儿加入,共同丰富 MXFlutter 动态化能力。数组
Dart 自己是描述语言,IDE 的 Outline 工具能够解析 Dart 代码生成树形结构,咱们能够利用其源码,生成 JSON UI 描述,相关代码:github.com/flutter/flu… dart-sdk: analysis_server缓存
静态解析 Dart 缺点,不能写逻辑,对编写UI代码有不少限制,不能写判断语句,不能写函数,要支持这些成本很高。因此只好放弃。数据结构
响应式UI框架架构
第一棵树有完整的UI描述信息,那么我只要JIT下经过 DartVM 建立第一棵树,其余耗时的操做都丢到AOT里去。
和方案一静态解析Dart对比,第二个方案是写一个极其轻量的运行时库,让编写UI的Dart 代码运行了起来,生成树形结构,再序列化为 JSON(debug),FlatBuffers (release)UI 描述。能够称之为动态解析方案
具体渲染逻辑
整体架构
架构也有了,方案也有了,要Run起来还有几个麻烦事要忙活,DartVM 要抽出来,Dart JIT层的轻量级运行时库,Dart AOT层把DSL转成真正Widget的UIEngin也要写哦,就是图中黄色和红色的三部分
Dart源代码在进行编译时会经过DART_PRECOMPILED_RUNTIME宏进行条件编译从而在Debug版编译JIT模式,Release版编译AOT模式。而且这两种模式是互斥的,没法同时存在。
咱们单独编译出一个DartVM,打包成动态库,修改导出符号,避免符合冲突
安装包过大,DartVM增大安装包30M,若是加上本来的AOT40M,整个Flutter安装包会增大到70M,用DartVM不现实。怎么办呢。
用JS开发假的Flutter Runtime 封装JavasSriptCore与Native、 Flutter互调接口
两个重要的数据结构
MXScriptWidget管理一个Script页面或控件,负责建立管理 ScriptWidgetTree,以自增ID与Flutter对应Widget相互调用 ,每次Build都会建立一个新的MXWidgetTree
在 JS 侧 buildWidget 时,咱们会对 function 事件,生成自增的惟一 callbackID,并与 widgetID 组合拼接成 widgetID/callbackID,做为事件的惟一标识。用户点击界面某个 button 时,事件由 Flutter 侧传到 JS 侧,经过解析 widgetID/callbackID,找到对应 widget 的 callback,完成事件处理。
经过在 JS 侧,ListView 调用 Build 方法时,提早展开 child, 并为 ListView 增长 children 成员变量。此时,由于仅有数据配置,不会有多余的 Layout 过程,因此速度是很是快的。
preBuild(jsWidget, buildContext) { if(this.builder) { for (let i = 0; i < this.childCount; ++i) { let w = this.builder(buildContext, i); this.children.push(w); } delete this.builder; } super.preBuild(jsWidget, buildContext); }
在 Flutter 侧,ListView 仍然是动态建立,滑动列表,MXFlutter Engine 根据 Children 数组里的配置数据,建立真正的 Flutter WidgetCell,效率与原生相同彻底一致。
ListView.builder( itemCount: children.length, itemBuilder: (context, index) { return UIEngine.toWidget(children[index]); }, )
动画参数在VM层配置一次,动画开始后在Flutter层闭环循环rebuild,造成动画效果,这个是比较通用的作法了。
无论JSWidget建立有多快,老是有跨语言执行,因此减小Build次数和减少Build出来的DSL UI描述大小,能够优化性能。
自动对比两次Widget 不管如何都没有直接建立一个新的快,若是开发者不参与,由框架来自动计算Diff是得不偿失的
牺牲响应式UI框架的设计模式 采用和Native、Web的方式,由开发者参与本身设置Diff的节点,即根据ID获取对应Widget,修改Widget参数,Rebuild生成新DSL
MXStatelessWidget 能够经过使用无状态的ScriptWidget来向框架标示,其下面的子树,在每次build中不会变化,其build结果会被缓存,下次在Flutter层直接复用
VM层,Flutter层,Native层镜像对象的生命周期如何控制?参考苹果 iOS JavaScriptCore 和 Objective-C的解决方法
参照业界RN等框架的设计,VM层跑在一个单独的后台线程
一个可行方案 修改FlutterEngine ,定制开发Dart->Native->VM 这个通道,调用到VM不切换线程 VM不新建线程,直接由Flutter UI Thread 消息循环驱动,这样也同时支持了和Flutter UI 层的高效同步调用,但要注意从Native调用到VM,须要经过定制FlutterEngine的接口。
让开发者写出优雅的代码,咳咳,这里有点吹了,总之,咱们想让使用MXFlutter的开发同窗写出来的代码看来正规一些,好看一些。
参考JS示例源码 TGIF-iMatrix home_page.js
由于 JavaScript 不支持模块化开发,不能引用其余文件代码,咱们参照 RN,使用 Node.js 的模块化代码,在Native 层支持 require 语法。开发时,IDE最好选用 VSCode,由于能够按装JS插件,直接运行调试JS另外,咱们经过重定向模拟器 JS 路径文件到开发机,用户修改完 JS 文件,即可直接看到相应修改,实现模拟器的页面热更新。
因为时间紧张,MXFlutter还有不少遗留的问题,做为一个技术探索,很是辛苦但很是有趣,期待各位大牛指导,期待小伙伴们提出问题一块儿讨论解决。
文章最后我给你们推荐一个最简单也是最有效的学习提高方法:脑图 + 视频 + 资料
在这也分享一份本身收录整理的阿里P6P7【安卓】进阶资料分享+加薪跳槽必备面试题 ,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每一个知识点专题都配有相对应的实战项目,能够有效的帮助你们掌握知识点。
总之也是在这里帮助你们学习提高进阶,也节省你们在网上搜索资料的时间来学习,也能够分享给身边好友一块儿学习
若是你有须要的话,能够点赞+评论,关注我, 加Vx:q1607947758(备注简书,须要进阶资料)