铜仁市论坛

首页 » 分类 » 分类 » 单招季软件技术专业用有限的代码,创造无
TUhjnbcbe - 2021/5/18 23:46:00
北京看白癜风哪里比较好 http://yyk.39.net/bj/zhuanke/89ac7.html

阿里妹导读:Flutter从本质上来讲还是一个UI框架,它解决的是一套代码在多端渲染的问题。在渲染管线的设计上更加精简,加上自建渲染引擎,相比ReactNative、Weex以及WebView等方案,具有更好的性能体验。本文将从架构和源码的角度详细分析Flutter渲染机制的设计与实现。较长,同学们可收藏后再看。

文末福利:下载《AliFlutter体系化建设和实践》电子书。

写在前面跨平台技术由于其一码多端的生产力提升而表现出巨大的生命力,从早期的HybridApp到ReactNative/Weex、小程序/快应用,再到现在的Flutter,跨平台技术一直在解决效率问题的基础上最大化的解决性能和体验问题。这也引出了任何跨平台技术都会面临的核心问题:

效率:解决在多应用、多平台、多容器上开发效率的问题,一码多端,业务快跑。

性能:解决的是业务的性能和体验问题。

效率作为跨平台技术的基本功能,大家都能做到。问题是谁能把性能和体验做得更好,在渲染技术这块一共有三种方案:

WebView渲染:依赖WebView进行渲染,在功能和性能上有妥协,例如PhoneGap、Cordova、小程序(有的小程序底层也采用了ReactNative等渲染方案)等。

原生渲染:上层拥抱W3C,通过中间层把前端框架翻译为原生控件,例如ReactNative+React、Weex+Vue的组合,这种方案多了一层转译层,性能上有损耗。随着原生系统的升级,在兼容性上也会有问题。

自建渲染:自建渲染框架,底层使用Skia等图形库进行渲染,例如Flutter、Unity。

Flutter由于其自建渲染引擎,贴近原生的实现方式,获得了优秀的渲染性能。Flutter拥有自己的开发工具,开发语言、虚拟机,编译机制,线程模型和渲染管线,和Android相比,它也可以看做一个小型的OS了。第一次接触Flutter,可以看看Flutter的创始人Eric之前的访谈《WhatisFlutter?》,Eric之前致力于Chromium渲染管线的设计与开发,因此Flutter的渲染与Chromium有一定的相似之处,后面我们会做下类比。后面我们会从架构和源码的角度分析Flutter渲染机制的设计与实现,在此之前也可以先看看Flutter官方对于渲染机制的分享《HowFlutterrendersWidgets》。视频+图文的方式会更加直观,可以有一个大体的理解。架构分析架构设计从结构上看,Flutter渲染由UIThread与GPUThread相互配合完成。

1)UIThread

对应图中1-5,执行DartVM中的Dart代码(包含应用程序和Flutter框架代码),主要负责WidgetTree、ElementTree、RenderObjectTree的构建,布局、以及绘制生成绘制指令,生成LayerTree(保存绘制指令)等工作。

2)GPUThread

对应图中6-7,执行Flutter引擎中图形相关代码(Skia),这个线程通过与GPU通信,获取LayerTree并执行栅格化以及合成上屏等操作,将LayerTree显示在屏幕上。

注:图层树(LayerTree)是Flutter组织绘制指令的方式,类似于AndroidRendering里的ViewDisplayList,都是组织绘制指令的一种方式。UIThread与GPUThread属于生产者和消费者的角色。流程设计我们知道Android上的渲染都是在VSync信号驱动下进行的,Flutter在Android上的渲染也不例外,它会向Android系统注册并等待VSync信号,等到VSync信号到来以后,调用沿着C++Engine-JavaEngine,到达DartFramework,开始执行Dart代码,经历Layout、Paint等过程,生成一棵LayerTree,将绘制指令保存在Layer中,接着进行栅格化和合成上屏。具体说来:1)向Android系统注册并等待VSync信号Flutter引擎启动时,会向Android系统的Choreographer(管理VSync信号的类)注册并接收VSync信号的回调。2)接收到VSync信号,通过C++Engine向DartFramework发起渲染调用当VSync信号产生以后,Flutter注册的回调被调用,VsyncWaiter::fireCallback()方法被调用,接着会执行Animator::BeiginFrame(),最终调用到Window::BeginFrame()方法,WIndow实例是连接底层Engine和DartFramework的重要桥梁,基本上与平台相关的操作都会通过Window实例来连接,例如input事件、渲染、无障碍等。3)DartFramework开始在UI线程执行渲染逻辑,生成LayerTree,并将栅格化任务post到GPU线程执行Window::BeiginFrame()接着调用,执行到RenderBinding::drawFrame()方法,这个方法会去驱动UI界面上的dirty节点(需要重绘的节点)进行重新布局和绘制,如果渲染过程中遇到图片,会先放到WorkerThead去加载和解码,然后再放到IOThread生成图片纹理,由于IOThread和GPIThread共享EGLContext,因此IOThread生成的图片纹理可以被GPUThread直接访问。4)GPU线程接收到LayerTree,进行栅格化以及合成上屏的工作DartFramework绘制完成以后会生成绘制指令保存在LayerTree中,通过Animator::RenderFrame()把LayerTree提交给GPUThread,GPUThread接着执行栅格化和上屏显示。之后通过Animator::RequestFrame()请求接收系统的下一次VSync信号,如此循环往复,驱动UI界面不断更新。逐个调用流程比较长,但是核心点没多少,不用纠结调用链,抓住关键实现即可,我们把里面涉及到的一些主要类用颜色分了个类,对着这个类图,基本可以摸清Flutter的脉络。绿色:Widget*色:Element红色:RenderObject以上便是Flutter渲染的整体流程,会有多个线程配合,多个模块参与,抛开冗长的调用链,我们针对每一步来具体分析。我们在分析结构时把Flutter的渲染流程分为了7大步,Flutter的timeline也可以清晰地看到这些流程,如下所示:注:ui代表UIThread,raster代表GPUThread。UIThread1)Animate由handleBeiginFrame()方法的transientCallbacks触发,如果没有动画,则该callback为空;如果有动画,则会回调Ticker.tick()触发动画Widget更新下一帧的值。2)Build由BuildOwner.buildScope()触发,主要用来构建或者更新三棵树,WidgetTree、ElementTree和RenderObjectTree。3)Layout由PipelineOwner.flushLayout()触发,它会调用RenderView.performLayout(),遍历整棵RenderTree,调用每个节点的layout(),根据build过程记录的信息,更新dirty区域RenderObject的排版数据,使得每个RenderObject最终都能有正确的大小(size)和位置(position,保存在parentData中)。4)CompositingBits由PipelineOwner.flushCompositingBits()触发,更新具有dirty合成位置的渲染对象,此阶段每个渲染对象都会了解其子项是否需要合成,在绘制阶段使用此信息选择如何实现裁剪等视觉效果。5)Paint由PipeOwner.flushPaint()触发,它会调用RenderView.paint()。最终触发各个节点的paint(),最终生成一棵LayerTree,并把绘制指令保存在Layer中。6)Submit(Compositing)由renderView.
1
查看完整版本: 单招季软件技术专业用有限的代码,创造无