字节跳动Flutter架构实践
Posted Flutter那些事
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了字节跳动Flutter架构实践相关的知识,希望对你有一定的参考价值。
前言
本文较长,不想看文字的也可以看视频,下面这个是配套的视频播放链接,点击即可播放:
1
移动跨平台技术探究
◆ 为什么需要跨平台?
◆ 跨平台技术是如何发展起来的?
◆ 跨平台技术选型有哪些?
◆ Flutter有什么独特优势(为什么选择Flutter)?
◆ 为什么说Flutter是高性能的,体现在哪里?
2
Flutter引擎原理剖析
先来看看Flutter的技术架构图:
C++引擎四个核心线程
Flutter 里四个核心线程:平台线程、UI 线程、GPU 线程、IO 线程,它们的职责都是不一样的:
★ 平台线程(PlatformThread)对应着安卓和 ios 的主线程。
★ UI线程(UI Thread)针对安卓本身的主线程,它就是一个独立的线程。
★ GPU线程(GPU Thread)运行在 GPU 上的线程,它主要是处理 Skia 相关的任务。
★ IO线程(IO Thread)主要处理IO有关的任务,比如:图片编解码等。
具体引擎架构图,如下图示所示:
Flutter如何编译成两个平台的应用程序
首先看下面的引擎代码是公共的部分,用于把程序的源代码编译android和IOS两个平台的应用程序。中间左侧绿色部分Flutter针对Android生成的一些文件,然后最终通过引擎会编译生成Android的APK安装包,中间右侧蓝色部分是Flutter针对IOS平台生成的一些文件,然后最终通过引擎会编译会生成IOS平台的安装包。
线程通信(混合开发必须了解的难点)
这里以Android为例,对照Flutter做一个讲解。Flutter里面的线程主要依赖于Dart里面的一些API,比如我们常用的异步任务里面,需要用到:MicroTask和Future,就是非常重要和关键的。
我们可以看到技术是相通的,Android里面是一个Handler,对应一个Looper Thread,然后是一个MessageQueue,然后里面存放的是Message。很巧妙的是Flutter也有类似的做法,Flutter有一个Looper线程,主线程复用了Native的,然后为其他三个线程创建独立的Looper。不过与Android不同的是:Flutter用的是两个队列,一个是微任务队列(MicroTaskQueue),一个是普通延迟任务队列。Flutter会先处理为任务队列,再去处理普通任务队列。类比Android来讲,图中的TaskRunner类似于Handler,PostTask就是把一个消息放到一个消息队列的过程。和Android的Handler是很类似的。所以在学新技术的时候,举一反三,懂得变通才能学的更好。
Dart虚拟机
同一个进程里可以有很多 Isolate,两个 Isolate 的堆是不能共享的。Dart开发团队早就考虑到了交互的问题,于是就设计了一个VM Isolate,它是一个用户Isolate之间交互的桥梁,运行在 UI 线程中的。我们可以把数据放在内核态,因为这个内核态可以共享数据,然后把数据放到另一方的队列中,另一方就可以拿到和使用这些数据了。熟悉Android的朋友们应该听起来很熟悉,这就非常类似我们熟知的“进程间通信”。
可能这样讲还不是很清楚,具体流程是什么样的呢?请看下图:
Isolate1是发送方,创建一个SendPort ,Isolate2是接收方,创建一个ReceivePort。我们创建一个 Isolate的时候,它里面有一个 worker 线程,worker 线程里面可以放入Task。SendPort调用send方法,发送数据到PortMap里面,这个里面每一个port对应一个Isolate 的 MessageHandler,这个Handler包括两个内容:普通的消息队列,一个是 OOB 高优先级消息,数据放入队列顺序有优先级区分。最后消息被封装成一个 MessageTask,传送到另一个Isolate里面去。回想一下是不是类似于Android的消息队列?
Platform Channels
Flutter提供了Channel是用于和Native功能做交互。Android这边使用MethodChannel,IOS这边使用FlutterMethdChannel。最终Flutter通过衔接两个平台,使用统一的规范去衔接两个平台,暴露出一些函数和接口,然后就可以很容易的使用Flutter去调用Native的功能了。这个依赖于开发者的水平,需要对Android或IOS比较了解才能更好的写出插件出来。pub.dev上面也有很多开源库,不过往往开发中需求总是在变更的,开源库远远达满足不了实际开发需求的功能点,所以还是需要自己掌握Platform Channels的知识点比较好。
3
字节跳动在Flutter架构上的实践
下面来看字节跳动主要做了哪些架构实践,请看下图:
从上图中还是可以看到字节跳动做了很多基础工作的,比如:容器化、混合工程、渲染、包体积、编译优化、多端一体化等。看上去确实感觉是很多东西的。下面简单的介绍其中比较关键的几个:
容器化架构
个人感觉所谓“容器化”,就类似于原生平台的组件化或者业务分层架构的思想。就是为普通业务打造可扩展的接口和行为准则。不过这个Flutter容器化架构要适应Android和IOS两个平台,然后针对不同的平台的业务行为需要定义统一的标准和规范以及封装了一些通用业务模块功能(其中的某些可以看做是基础业务的基类),比如:图片调用,直接去这里面的协议层,直接调用就可以了,内部都封装好了,直接传参调用,这里面有默认的适配,你也可以自定义。有了“容器化”的架构,平台基础API的差异性就不需要考虑了,开发者只管调用内部的功能模块或者接口即可快速开发新需求功能。
多端一体化实践
这个就不用多说了,就是写一个应用可以同时运行在Android、IOS、Web上面,省去了大量的开发周期,多端一体化结合了Flutter,以及Flutter Web,然后定制化了一些内部引擎和功能模块的架构,最终打造出了这套多端一体化的工程体系架构。
监控体系
Flutter自带的性能监控工具颗粒度是很粗糙的,用的是物理平均,而不能反映真实的每一帧实时统计。UI线程和GPU线程的处理方式不一样,这样物理平均确实是不直观的做法。
业界很多公司通用的做法是框架层去统计UI线程耗时时间,消息 post 到 UI,然后再post到GPU。但是这种做法有两个缺陷:一个是等待时间没有考虑进去,一个是UI 线程非常快,但是 GPU 线程非常慢,UI 线程向 GPU 线程跑消息时最多 Post 两个消息,这时候 GPU 线程依然处理不过来,UI 线程就不会 Post 消息,但是 UI 线程体现不出来。
字节跳动采用的是“高精度无侵入性能监控方案”:引擎层提供了一套机制,可以知道绘制多少帧,统计你发了多少信号,统计 GPU 线程。另外“无侵入”体现在:框架系统会自动识别性能监控滚动会在什么时候开始,什么时候结束。
Flutter Turbo
这个主要是性能提升的方案,比如:消息调度、GC抑制、关闭Semantics、关闭抗锯齿等,另外为了提高性能,内部有一个 Benchmark 跑分,有了这些方案支持,可以大大的提升应用程序额性能。
图片透传优化方案
我个人觉得这个方案还是挺不错的,非常有借鉴价值,值得学习和研究一下。
现有的方式 Image.network加载网络图片,传入一个路径就可以加载了,然后Dart底层引擎层做解码操作。
另外外接纹理方案也是不错的,也是可以实现的,但是它没有在这个基础上进行改造,性能上还有提升空间。
字节跳动的透传方案是这样的:使用框架加载图片,然后生成Bitmap,然后Native和Dart VM共享Bitmap,而不是直接的数据传过来的,然后Dart引擎再转成Pixel buffer,这样一来对性能有很大的提升。
包体积优化
大概有这些优化:做了编译优化;对Flutter产物的数据端做了压缩,对Skia做了裁剪,不需要的东西删掉了,引擎库对一些功能三方库也做了裁剪。
【虽说看的云里雾里,感觉还是很厉害的样子。类型一万个:“卧槽,这么牛逼!”】
在这次会议上的第3个主题演讲中,来自字节跳动的专家还专门对包体积优化做了讲解,我会在后面的文章再整理一下分享给大家。
启动速度优化
修改了引擎代码,对启动速度做了优化,几乎提升了1倍。(虽然很牛逼,但是看不到摸不着,也不知道是如何修改的,我只能说希望早点贡献给Flutter社区,我们这些吃瓜群众都来享一下福,沾一点光啊。)
【干货好文】
在这里也欢迎各位多提意见。如果大家有什么意见或建议,欢迎在公众号里面留言,感谢大家的支持。
长按二维码关注,随时获取技术干货!
好看你就
点点
我
以上是关于字节跳动Flutter架构实践的主要内容,如果未能解决你的问题,请参考以下文章