Flutter是谷歌开源的移动UI框架,可以快速在Android和iOS上构建出高质量的原生用户界面,目前全世界越来越多的开发者加入到Flutter的队伍。Flutter相比RN性能更好,由于Flutter自己实现了一套UI框架,丢弃了原生的UI框架,非常接近原生的体验。 我们知道Flutter在UI绘制方面的效率是几乎接近原生的,这点比ReactNative要优秀很多,因为ReactNative是通过桥接转换然后去调用各自平台的UI系统(如iOS中的UIKit框架)提供的API来完成绘图。一、UI线程渲染 为了揭秘Flutter高性能,本文从源码角度来看看Flutter的渲染绘制机制,跟渲染直接相关的两个线程是UI线程和GPU线程: UI线程:运行着UITaskRunner,是FlutterEngine用于执行Dartrootisolate代码,将其转换为layertree视图结构; GPU线程:该线程依然是在CPU上执行,运行着GPUTaskRunner,处理layertree,将其转换成为GPU命令并发送到GPU。 通过VSYNC信号使UI线程和GPU线程有条不紊的周期性的渲染界面,本文介绍VSYNC的产生过程、UI线程在引擎和框架的绘制工作,下一篇文章会介绍GPU线程的绘制工作。1。1UI渲染原理 私信发送核心笔记或手册;即可免费领取Flutter3。0学习进阶资料!1。1。1UI渲染概览 通过VSYNC信号使UI线程和GPU线程有条不紊的周期性的渲染界面,如下图所示: 当需要渲染则会调用到Engine的ScheduleFrame()来注册VSYNC信号回调,一旦触发回调doFrame()执行完成后,便会移除回调方法,也就是说一次注册一次回调; 当需要再次绘制则需要重新调用到ScheduleFrame()方法,该方法的唯一重要参数regeneratelayertree决定在帧绘制过程是否需要重新生成layertree,还是直接复用上一次的 UI线程的绘制过程,最核心的是执行WidgetsBinding的drawFrame()方法,然后会创建layertree视图树 再交由GPUTaskRunner将layertree提供的信息转化为平台可执行的GPU指令。1。1。2UI绘制核心工作 1)Vsync单注册模式:保证在一帧的时间窗口里UI线程只会生成一个layertree发送给GPU线程,原理如下: Animator中的信号量pendingframesemaphore用于控制不能连续频繁地调用Vsync请求,一次只能存在Vsync注册。 pendingframesemaphore初始值为1,在Animator::RequestFrame()消费信号会减1,当而后再次调用则会失败直接返回; Animator的BeginFrame()或者DrawLastLayerTree()方法会执行信号加1操作。 2)UI绘制最核心的方法是drawFrame(),包含以下几个过程: Animate:遍历transientCallbacks,执行动画回调方法; Build:对于dirty的元素会执行build构造,没有dirty元素则不会执行,对应于buildScope() Layout:计算渲染对象的大小和位置,对应于flushLayout(),这个过程可能会嵌套再调用build操作; Compositingbits:更新具有脏合成位的任何渲染对象,对应于flushCompositingBits(); Paint:将绘制命令记录到Layer,对应于flushPaint(); Compositing:将Compositingbits发送给GPU,对应于compositeFrame(); Semantics:编译渲染对象的语义,并将语义发送给操作系统,对应于flushSemantics()。 UI线程的耗时从doFrame(frameTimeNanos)中的frameTimeNanos为起点,以小节〔4。10。6〕Animator::Render()方法结束为终点,并将结果保存到LayerTree的成员变量constructiontime,这便是UI线程的耗时时长。1。1。3Timeline说明 3)以上几个过程在Timeline中ui线程中都有体现,如下图所示: 另外Timeline中还有两个比较常见的标签项 FrameRequestPending:从Animator::RequestFrame到Animator::BeginFrame()结束; PipelineProduce:从Animator::BeginFrame()到Animator::Render()结束。二、渲染过程构建Widgets 首先观察以下的代码片段,它代表了一个简单的widget结构:Container(color:Colors。blue,child:Row(children:〔Image。network(https:www。example。com1。png),Text(A),〕,),); 当Flutter需要绘制这段代码片段时,框架会调用build()方法,返回一棵基于当前应用状态来绘制UI的widget子树。在这个过程中,build()方法可能会在必要时,根据状态引入新的widget。在上面的例子中,Container的color和child就是典型的例子。我们可以查看Container的源代码,你会看到当color属性不为空时,ColoredBox会被加入用于颜色布局。if(color!null)currentColoredBox(color:color!,child:current); 与之对应的,Image和Text在构建过程中也会引入RawImage和RichText。如此一来,最终生成的widget结构比代码表示的层级更深,在该场景中如下图2: 这就是为什么你在使用DartDevTools的Flutterinspector调试widget树结构时,会发现实际的结构比你原本代码中的结构层级更深。从Widget到Element 在构建的阶段,Flutter会将代码中描述的widgets转换成对应的Element树,每一个Widget都有一个对应的Element。每一个Element代表了树状层级结构中特定位置的widget实例。目前有两种Element的基本类型: ComponentElement,其他Element的宿主。 RenderObjectElement,参与布局或绘制阶段的Element。 RenderObjectElement是底层RenderObject与对应的widget之间的桥梁。 任何widget都可以通过其BuildContext引用到Element,它是该widget在树中的位置的上下文。类似Theme。of(context)方法调用中的context,它作为build()方法的参数被传递。 由于widgets以及它上下节点的关系都是不可变的,因此,对widget树做的任何操作(例如将Text(‘A’)替换成Text(‘B’))都会返回一个新的widget对象集合。但这并不意味着底层呈现的内容必须要重新构建。Element树每一帧之间都是持久化的,因此起着至关重要的性能作用,Flutter依靠该优势,实现了一种好似widget树被完全抛弃,而缓存了底层表示的机制。Flutter可以根据发生变化的widget,来重建需要重新配置的Element树的部分。布局和渲染 很少有应用只绘制单个widget。因此,有效地排布widget的结构及在渲染完成前决定每个Element的大小和位置,是所有UI框架的重点之一。 在渲染树中,每个节点的基类都是RenderObject,该基类为布局和绘制定义了一个抽象模型。这是再平凡不过的事情:它并不总是一个固定的大小,甚至不遵循笛卡尔坐标规律(根据该极坐标系的示例所示)。每一个RenderObject都了解其父节点的信息,但对于其子节点,除了如何访问和获得他们的布局约束,并没有更多的信息。这样的设计让RenderObject拥有高效的抽象能力,能够处理各种各样的使用场景。 在构建阶段,Flutter会为Element树中的每个RenderObjectElement创建或更新其对应的一个从RenderObject继承的对象。RenderObject实际上是原语:渲染文字的RenderParagraph、渲染图片的RenderImage以及在绘制子节点内容前应用变换的RenderTransform是更为上层的实现。更新UI 上面介绍了Flutter在Framework级别渲染的流程(后续交给图像引擎),这只是一帧的流程。Flutter在更新UI上也有一些不同。更新WidgetTree 之前提到了Widget的不可变性,所以每一帧都会调用build()方法返回widget树,即使是StatefulWidget,也只是根据不同的状态返回不同的widget树。但这样的话理论上会有大量的实例的产生和销毁,频繁GC,不过实际上,上面提到了Widget只是UI的配置,不负责实际的渲染,开销并没有那么大。更新ElementTree 更重量级的ElementTree并不会全部重新渲染,而是根据WidgetTree的变化维护ElementTree(插入、删除、更新、移动),其中核心的几个方法包括:1。Element调用Widget。canUpdate(),去判断新的widget是否能用于更新Element。staticboolcanUpdate(WidgetoldWidget,WidgetnewWidget){returnoldWidget。runtimeTypenewWidget。runtimeTypeoldWidget。keynewWidget。}2。可以更新的话调用Element。update()Element。updateChild()继承类自行实现mustCallSupervoidupdate(covariantWidgetnewWidget){widgetnewW}framework。dartprotectedElementupdateChild(Elementchild,WidgetnewWidget,dynamicnewSlot){if(newWidgetnull){if(child!null)deactivateChild(child);}ElementnewCif(child!null){assert((){finalintoldElementClassElement。debugConcreteSubtype(child);finalintnewWidgetClassWidget。debugConcreteSubtype(newWidget);hasSameSuperclassoldElementClassnewWidgetC}());if(hasSameSuperclasschild。widgetnewWidget){if(child。slot!newSlot)updateSlotForChild(child,newSlot);newC}elseif(hasSameSuperclassWidget。canUpdate(child。widget,newWidget)){if(child。slot!newSlot)updateSlotForChild(child,newSlot);child。update(newWidget);assert(child。widgetnewWidget);assert((){child。owner。debugElementWasRebuilt(child);}());newC}else{deactivateChild(child);assert(child。parentnull);newChildinflateWidget(newWidget,newSlot);}}else{newChildinflateWidget(newWidget,newSlot);}assert((){if(child!null)debugRemoveGlobalKeyReservation(child);finalKeykeynewWidget?。if(keyisGlobalKey){key。debugReserveFor(this,newChild);}}());returnnewC}。。。staticboolcanUpdate(WidgetoldWidget,WidgetnewWidget){returnoldWidget。runtimeTypenewWidget。runtimeTypeoldWidget。keynewWidget。} newWidgetnull说明子节点对应的Widget已被移除,直接removechildelement(如有);childnull说明newWidget是新插入的,创建子节点(inflateWidget);child!null此时,分为3种情况:若child。widgetnewWidget,说明child。widget前后没有变化,若child。slot!newSlot表明子节点在兄弟结点间移动了位置,通过updateSlotForChild修改child。slot即可;通过Widget。canUpdate判断是否可以用newWidget修改childelement,若可以,则调用update方法;否则先将childelement移除,并通newWidget创建新的element子节点。更新RenderObjectTreeDartVM DartVM的内存回收机制采用多生代无锁垃圾回收器,专门为UI框架中常见的大量Widgets对象创建和销毁优化。基本的流程:DartVM的内存分配策略非常简单,创建对象时只需要在现有堆上移动指针,内存增长始终是线形的,省去了查找可用内存段的过程: Dart中类似线程的概念叫做Isolate,每个Isolate之间是无法共享内存的,所以这种分配策略可以让Dart实现无锁的快速分配。Dart的垃圾回收也采用了多生代算法,新生代在回收内存时采用了半空间算法,触发垃圾回收时Dart会将当前半空间中的活跃对象拷贝到备用空间,然后整体释放当前空间的所有内存: 整个过程中Dart只需要操作少量的活跃对象,大量的没有引用的死亡对象则被忽略,这种算法也非常适合Flutter框架中大量Widget重建的场景。 flutter基础到精通学习;dart语法基础到flutterUI、线程、启动流程、框架、性能监控等。 私信获取全部学习资料! 文末 文章主要解析了flutter的UI线程渲染原理,以及渲染其过程。关于更多flutter的进阶学习。比喻(flutter动画原理、渲染机制、通信机制等。这只是部分)可以上面直达得到。 私信:核心笔记或手册即可领取哦!