}),

});

}

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,已经接近在线上版直接使用了。

这个漂亮的知乎页面,是用Dart版转JS而来,在此鸣谢作者许吉友 ,大家可以关注一下他。

现状

MXFlutter虽然各个模块已相对完整,但投入生产还需要解决其中的BUG,由于19年初,小组启动新项目,非常繁忙,几乎没有时间继续开发,从3月份一直暂停,目前人力仍然很紧张,所以先放出技术方案和预览版代码,如果大家有兴趣,期待小伙伴们一起加入,共同丰富 MXFlutter 动态化能力。

0x00 分享下动态化探索过程中的几个炮灰方案
Flutter 动态化方案一:静态解析Dart语言,生成UI描述

Dart 本身是描述语言,IDE 的 Outline 工具可以解析 Dart 代码生成树形结构,我们可以利用其源码,生成 JSON UI 描述,相关代码:github.com/flutter/flu… dart-sdk: analysis_server

静态解析 Dart 缺点,不能写逻辑,对编写UI代码有很多限制,不能写判断语句,不能写函数,要支持这些成本很高。所以只好放弃。

快速介绍下Flutter的核心渲染模块三棵树

响应式UI框架

  1. WidgetTree:Widget 里面存储了一个视图的配置信息,可以高效的创建(build)和销毁

  2. Element 是分离 WidgetTree 和真正的渲染对象的中间层, WidgetTree 用来描述对应的Element 属性

  3. RenderObject 来执行 Diff, Hit Test 布局、绘制

第一棵树有完整的UI描述信息,那么我只要JIT下通过 DartVM 创建第一棵树,其他耗时的操作都丢到AOT里去。

Flutter 动态化方案二:动态运行 Dart 语言,生产UI描述

和方案一静态解析Dart对比,第二个方案是写一个极其轻量的运行时库,让编写UI的Dart 代码运行了起来,生成树形结构,再序列化为 JSON(debug),FlatBuffers (release)UI 描述。可以称之为动态解析方案

具体渲染逻辑

总体架构

架构也有了,方案也有了,要Run起来还有几个麻烦事要忙活,DartVM 要抽出来,Dart JIT层的轻量级运行时库,Dart AOT层把DSL转成真正Widget的UIEngin也要写哦,就是图中黄色和红色的三部分

抽离DartVM

无法简单修改编译条件抽离

Dart源代码在进行编译时会通过DART_PRECOMPILED_RUNTIME宏进行条件编译从而在Debug版编译JIT模式,Release版编译AOT模式。并且这两种模式是互斥的,无法同时存在。

简单的解决方法是

我们单独编译出一个DartVM,打包成动态库,修改导出符号,避免符合冲突

引入DartVM还需要的工作
  • 开发DartVM与Native互通接口,参考了Flutter,使用Native Extension和Dart_Invoke实现互相调用

  • 双DartVM调试方案,两个DartVM独立运行,通过远程端口单独调试DartFlutter

  • 支持引入第三方库,DartFlutter在打包发布时会通过shell脚本分析.packages文件将依赖库自动打包随Dart File Zip一起随包下发。 常用库可以预先打包的App本地,减少下发文件大小

一个暂时无法解决的问题

安装包过大,DartVM增大安装包30M,如果加上原本的AOT40M,整个Flutter安装包会增大到70M,用DartVM不现实。怎么办呢。

0x01 最终方案JavasSriptCore 替换DartVM


可性能分析

  1. JavasSriptCore 是iOS官方库,不增加安装包

  2. Dart代码和JS代码非常相近,可以用工具转换

  3. JavasSriptCore 与 Native有更方便的互调接口

  4. ReactNative 已验证通过JS开发App能力是可行的

  5. JS的执行效率是DartVM的3倍编码1M的JSON只需 2毫秒

需要解决的问题

用JS开发假的Flutter Runtime 封装JavasSriptCore与Native、 Flutter互调接口

0x02 讲解下MXFlutter的渲染原理


渲染树

两个重要的数据结构

  • MXScriptWidget

  • MXWidgetTree

MXScriptWidget管理一个Script页面或控件,负责创建管理 ScriptWidgetTree,以自增ID与Flutter对应Widget相互调用 ,每次Build都会创建一个新的MXWidgetTree

MXFlutter 事件

在 JS 侧 buildWidget 时,我们会对 function 事件,生成自增的唯一 callbackID,并与 widgetID 组合拼接成 widgetID/callbackID,作为事件的唯一标识。用户点击界面某个 button 时,事件由 Flutter 侧传到 JS 侧,通过解析 widgetID/callbackID,找到对应 widget 的 callback,完成事件处理。

MXFlutter 高效的动态列表

通过在 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]);

},

)

MXFlutter 动画的方案

动画参数在VM层配置一次,动画开始后在Flutter层闭环循环rebuild,形成动画效果,这个是比较通用的做法了。

0x03 渲染优化

不管JSWidget创建有多快,总是有跨语言执行,所以减少Build次数和减小Build出来的DSL UI描述大小,可以优化性能。

渲染优化1-局部刷新:配置树Diff
一个事实

自动对比两次Widget 无论如何都没有直接创建一个新的快,如果开发者不参与,由框架来自动计算Diff是得不偿失的

可行的方法

牺牲响应式UI框架的设计模式 采用和Native、Web的方式,由开发者参与自己设置Diff的节点,即根据ID获取对应Widget,修改Widget参数,Rebuild生成新DSL

渲染优化2-局部刷新-嵌套节点
  • MXScriptWidget 是一个具备Build WidgetTree,缓存Callback映射表,动画支持的基本单位。可以作为普通FlutterWidget来使用。

  • 在Flutter层,如果Widget树中节点有MXScriptWidget,则在对应节点上创建MXFlutterWidget自定义控件

  • 两个子树可以相互对应获得局部刷新,callback回调,动画支持,Rebuild时所生产的UI DSL 大大减少,加快刷新速率

渲染优化3-可以分离动态和静态控件

MXStatelessWidget 可以通过使用无状态的ScriptWidget来向框架标示,其下面的子树,在每次build中不会变化,其build结果会被缓存,下次在Flutter层直接复用image

内存-跨层镜像对象的生命周期

VM层,Flutter层,Native层镜像对象的生命周期如何控制?参考苹果 iOS JavaScriptCore 和 Objective-C的解决方法

  1. 以Flutter层的对象生命周期为主

  2. 在VM层增加WeakMap支持,不增加对象引用计数,Flutter层释放之后,释放VM层对象

  3. 在Native层使用 JSManagerValue,VM层对象释放后,Native的引用被自动置空

image

线程问题

参照业界RN等框架的设计,VM层跑在一个单独的后台线程

  1. 从Flutter层通过Native通道调用到VM,发生两次线程切换

  2. Flutter UI层和MXScript层是异步调用,限制动态控件的架构设计

一个可行方案 修改FlutterEngine ,定制开发Dart->Native->VM 这个通道,调用到VM不切换线程 VM不新建线程,直接由Flutter UI Thread 消息循环驱动,这样也同时支持了和Flutter UI 层的高效同步调用,但要注意从Native调用到VM,需要通过定制FlutterEngine的接口。

0x04 让开发者写出优雅的代码

让开发者写出优雅的代码,咳咳,这里有点吹了,总之,我们想让使用MXFlutter的开发同学写出来的代码看来正规一些,好看一些。

  • 完美支持Dart Flutter语法

  • 定义所有Flutter 中同名Widget类,构建Widget的参数类,支持相同的Build方式,SetState触发刷新,事件响应函数

  • Callback函数自动生成CallbackID

  • Callback函数自动This绑定

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

img

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-Wqzk6gra-1713689667485)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐