跨平台技术演进及 Flutter 未来 | 开发者说·DTalk
Posted 谷歌开发者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跨平台技术演进及 Flutter 未来 | 开发者说·DTalk相关的知识,希望对你有一定的参考价值。
本文原作者: Gityuan,原文发布于微信公众号 Gityuan: https://mp.weixin.qq.com/s/UZQU-PWM5JEb3dYtZKT6Cw
移动跨平台技术演进
从上图可以看出,技术演进过程大致分以下三个阶段:
Flutter 技术优势
1. 高效率: 采用 Dart 语言编写代码,虽然刚开始上手需要点时间,但熟练后效率比较高。一套代码适用多个平台 (Android、iOS、Web),以及高效的 Hot Reload 能快速辅助调试;
2. 动态化: 2017 年 3 月苹果下发警告邮件,禁止 JSPatch 等 iOS App 热更新方案,从此 iOS 动态化成为一个不宜公开讨论的话题。同样地,Flutter 引擎在某一个官方版本对动态化做过一些尝试,但后续基于风险考虑移除,当然并没有阻碍大家对技术的探索,这里不方便展开讨论;
3. 高一致性: 实现 UI 像素级的控制,Flutter 渲染引擎依靠跨平台 Skia 图形库来实现,仅依赖系统图形绘制相关的接口,比如未来 Android 会支持 Vulkan,iOS 会支持 Metal,这些都是通过 Skia 封装调用。可最大程度上保证不同平台的体验一致性,见下图所示。
4. 高性能: 渲染性能优于现有的各种跨平台框架,可媲美原生性能的跨平台技术方案,Dart 代码执行效率比 JS 高,通过 AOT 编译成平台原生代码,渲染采用自渲染 Skia 方案,既不需要 JS Bridge 桥接,也不需要 Art 虚拟机参与。再从渲染原理来看看 Flutter 的高性能的底气在哪里。
图解:
Android 原生框架,通过调用 Java Framework 层,再调用到 Skia 来渲染界面;
其他跨平台方案 (如 RN),通过 JSBridge 中间层来将 JS 写的 APP 转换成相应的原生渲染逻辑,可见比 Native 代码增加了更多逻辑,性能逊色差于原生框架;
Flutter 框架,APP 通过调用 Dart Framework 层,再直接调用到 Skia 来渲染界面,并没有经过原生 Framework 过程,可见其渲染性能并不会弱于 Native 技术,这是一个性能上限很高的跨平台技术。
目前,全球越来越多的公司已经在大家耳熟能详的知名 APP 中使用 Flutter 技术并落地,尤其国内知名互联网公司对 Flutter 投入度很大,社区也是非常活跃。
Flutter 未来趋势
目前 Flutter 主要在移动 Android/iOS 跨双端,Flutter 的愿景是成为一个多端运行的 UI 框架,能够支持不仅仅是移动端,还包括 Web、桌面、甚至嵌入式设备。在 2019 Google I/O 开发者大会上推出的使用 Flutter 开发 Web 应用的框架,同年 9 月发布 Flutter 1.9,并将 Flutter Web 合入 Flutter 主仓库。
从架构图看,Flutter 采用同一个 Dart Framework 层来统一 Flutter C++ 引擎和 Web 引擎,最终可以运行在 Android,iOS,Browser 上,从 Flutter 引擎代码不难看出 Flutter 也是支持 Fuchsia 操作系统。
Fuchsia 是 Google 内部正在开发的一款新的操作系统,采用 Flutter 作为系统默认的 UI 框架,也就是说 Flutter 天然支持 Fuchsia,这无疑让 Flutter 在众多的跨平台方案更有优势。
从 Fuchsia 技术架构来看,内核层 Zircon 的基础 LK 是专为嵌入式应用中小型系统设计的内核,代码简洁,适合嵌入式设备和高性能设备,比如 IoT、移动可穿戴设备等,目前这些领域还没有标准化级别的垄断者。以及在框架层中有着语音交互、云端以及智能化等模块,由此笔者揣测未来 Fuchsia 率先应用在音控等智能嵌入式设备。
Flutter 引擎架构
Flutter Framework 层: 用 Dart 编写,封装整个 Flutter 架构的核心功能,包括 Widget、动画、绘制、手势等功能,有 Material (Android 风格 UI) 和 Cupertino (iOS 风格) 的 UI 界面, 可构建 Widget 控件以及实现 UI 布局。
Flutter Engine 层: 用 C++ 编写,用于高质量移动应用的轻量级运行时环境,实现了 Flutter 的核心库,包括 Dart 虚拟机、动画和图形、文字渲染、通信通道、事件通知、插件架构等。引擎渲染采用的是 2D 图形渲染库 Skia,虚拟机采用的是面向对象语言 Dart VM ,并将它们托管到 Flutter 的嵌入层。Shell 实现了平台相关的代码,比如跟屏幕键盘 IME 和系统应用生命周期事件的交互。不同平台有不同的 Shell,比如 Android 和 iOS 的 Shell。
Flutter 编译产物
看完 Flutter 内部架构,或许你好奇,Flutter 不用 Android/iOS 的本地语言技术开发,Dart 编写完的代码如何让不同系统可以识别,最终编译后得到的产物是什么呢?
Flutter 引擎启动
这里以 Android 为例,熟悉 Android 的开发者,应该都了解 APP 启动过程,会执行 Application 和 Activity 的 onCreate() 方法,FlutterApplication 和 FlutterActivity 的onCreate() 方法正是连接 Native 和 Flutter 的枢纽。
FlutterApplication.java 的 OnCreate 过程主要完成初始化配置、加载引擎 libflutter.so、注册 JNI 方法;
FlutterActivity.java 的 onCreate 过程,通过 FlutterJNI 的 AttachJNI() 方法来初始化引擎 Engine、Dart 虚拟机、Isolate、Task Runner 等对象。再经过层层处理最终调用 main.dart 中 main() 方法,执行 runApp (Widget app) 来处理整个 Dart 业务代码。
Flutter 引擎启动中会创建有 4 个 Task Runner 以及创建虚拟机,分别来看看它们的工作原理。
Flutter 引擎启动过程,会创建 UI/GPU/IO 这 3 个线程,会为这些线程依次创建 MessageLoop 对象,启动后处于 epoll_wait 等待状态。对于 Flutter 的消息机制跟 Android 原生的消息机制有很多相似之处,都有消息 (或者任务)、消息队列 (或任务队列) 以及 Looper;有一点不同的是 Android 有一个 Handler 类,用于发送消息以及执行回调方法,相对应 Flutter 中有着相近功能的便是 TaskRunner。
上图是从源码中提炼而来的任务处理流程,比官方流程图更容易理解一些复杂流程的时序问题,后续会专门讲解个中原由。Flutter 的任务队列处理机制跟 Android 的消息队列处理相通,只不过 Flutter 分为 Task 和 MicroTask 两种类型,引擎和 Dart 虚拟机的事件以及 Future 都属于 Task,Dart 层执行 scheduleMicrotask() 所产生的属于 Microtask。
每次 Flutter 引擎在消费任务时调用 FlushTasks() 方法,遍历整个延迟任务队列 Delayed tasks,将已到期的任务加入 Task 队列,然后开始处理任务。
Step 1: 检查 Task,当 Task 队列不为空,先执行一个 Task;
Step 2: 检查 microatsk,当 microtask 不为空,则执行 microtask;不断循环 Step 2 直到 microtask 队列为空,再回到执行 Step 1;
可简单理解为先处理完所有的 Microtask,然后再处理 Task。因为 scheduleMicrotask() 方法的调用自身就处于一个 Task,执行完当前的 Task,也就意味着马上执行该 Microtask。
1. Platform Task Runner: 运行在 Android 或者 iOS 的主线程,尽管阻塞该线程并不会影响 Flutter 渲染管道,平台线程建议不要执行耗时操作;否则可能触发 watchdog 来结束该应用。比如 Android、iOS 都是使用平台线程来传递用户输入事件,一旦平台线程被阻塞则会引起手势事件丢失。
2. UI Task Runner: 运行在 UI 线程,比如 1.ui,用于引擎执行 root isolate 中的所有 Dart 代码,执行渲染与处理 Vsync 信号,将 Widget 转换生成 Layer Tree。除了渲染之外,还有处理 Native Plugins 消息、Timers、Microtasks 等工作;
3. GPU Task Runner: 运行在 GPU 线程,比如 1.gpu,用于将 Layer Tree 转换为具体 GPU 指令,执行设备 GPU 相关的 Skia 调用,转换相应平台的绘制方式,比如 OpenGL, Vulkan, Metal 等。每一帧的绘制需要 UI Runner 和 GPU Runner 配合完成,任何一个环节延迟都可能导致掉帧;
4. IO Task Runner: 运行在 IO 线程,比如 1.io,前 3 个 Task Runner 都不允许执行耗时操作,该 Runner 用于将图片从磁盘读取出来,解压转换为 GPU 可识别的格式后,再上传给 GPU 线程。为了能访问 GPU,IO Runner 跟 GPU Runner 的 Context 在同一个 ShareGroup。比如 UIImage 通过异步调用让 IO Runner 来异步加载图片,该线程不能执行其他耗时操作,否则可能会影响图片加载的性能。
图解:
Isolate 堆是运该 Isolate 中代码分配的所有对象的 GC 管理的内存存储;
VM Isolate 是一个伪 Isolate,里面包含不可变对象,比如 null,true,false;
Isolate 堆能引用 VM Isolate 堆中的对象,但 VM Isolate 不能引用 Isolate 堆;
Isolate 彼此之间不能相互引用;
-
每个 Isolate 都有一个执行 Dart 代码的 Mutator thread,一个处理虚拟机内部任务 (比如 GC, JIT 等) 的 Helper Thread;可见,Isolate 是拥有内存堆和控制线程,虚拟机中可以有很多 Isolate,但彼此之间内存不共享,无法直接访问,只能通过 Dart 特有的 Port 端口通信;Isolate 除了拥有一个 Mutator 控制线程,还有一些其他辅助线程,比如后台 JIT 编译线程、GC 清理/并发标记线程;
Widget 架构概览
-
StatelessWidget: 内部没有保存状态,UI 界面创建后不会发生改变; -
StatefulWidget: 内部有保存状态,当状态发生改变,调用 setState() 方法会触发 StatefulWidget 的 UI 发生更新,对于自定义继承自 StatefulWidget 的子类,必须要重写 createState() 方法。
三棵树
-
Widget 是为 Element 描述需要的配置,负责创建 Element,决定 Element 是否需要更新。Flutter Framework 通过差分算法比对 Widget 树前后的变化,决定 Element 的 State 是否改变。当重建 Widget 树后并未发生改变, 则 Element 不会触发重绘,则就是 Widget 树的重建并不一定会触发 Element 树的重建。 -
Element 表示 Widget 配置树的特定位置的一个实例,同时持有 Widget 和 RenderObject,负责管理 Widget 配置和 RenderObject 渲染。Element 状态由 Flutter Framework 管理, 开发人员只需更改 Widget 即可。 -
RenderObject 表示渲染树的一个对象,负责真正的渲染工作,比如测量大小、位置、绘制等都由 RenderObject 完成。
渲染过程,UI 线程完成布局、绘制操作,生成 Layer Tree;GPU 线程执行合成并光栅化后交给 GPU 来处理,其中几个关键步骤:
Animate: 遍历 _transientCallbacks,执行动画回调方法;
Build: 对于 dirty 的元素会执行 build 构造,没有 dirty 元素则不会执行,对应于 buildScope()
Layout: 计算渲染对象大小和位置,对应于 flushLayout(),这个过程可能会嵌套再调用 build 操作;
Compositing bits: 更新具有脏合成位的任何渲染对象, 对应于 flushCompositingBits();
Paint: 将绘制命令记录到 Layer, 对应于 flushPaint();
Compositing: 将 Compositing bits 发送给 GPU, 对应于 compositeFrame();
Platform Channels
结束语
-
编译技术如何将 Dart 代码转换为 AST (抽象语法树),如何汇编转换为机器码,打包成产物是什么? -
Flutter 这台引擎如何发动的,怎么跟 Native 原生系统衔接运行,如何识别产物并加载到内存? -
引擎启动后,Task Runner 如何分发任务,跟原生系统消息机制有什么关系? -
Dart 虚拟机如何管理内存,跟 isolate 又有什么关系? -
开发者编写的 Widget 控件如何渲染到屏幕上? -
Flutter 如何通过 plugin 支持移动设备提供的服务?
"开发者说·DTalk" 面向中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。
点击屏末 | 阅读原文 | 了解更多 "开发者说·DTalk" 活动详情与参与方式
长按右侧二维码
报名参与
以上是关于跨平台技术演进及 Flutter 未来 | 开发者说·DTalk的主要内容,如果未能解决你的问题,请参考以下文章
移动互联网这十年,跨平台技术的演进及 Flutter 的未来
Flutter 深入探索混合开发的技术演进 | 开发者说·DTalk
Flutter 深入探索混合开发的技术演进 | 开发者说·DTalk