做一个高一致性、高性能的Flutter动态渲染,真的很难么?

在解决Flutter动态化模板渲染的性能问题后,本文探讨如何提升渲染一致性。通过分析现有架构的问题,提出了以RenderObject为中心的新版渲染架构,引入MeasureSpecMode概念,详细阐述了新版渲染架构的设计和实现,包括非布局空间和布局空间的performLayout方法,展示了如何在不牺牲性能的前提下提升Flutter与Native的渲染一致性。
摘要由CSDN通过智能技术生成

最近小组在尝试使用集团DinamicX的DSL,通过下发DSL模板实现Flutter端的动态化模板渲染。在解决了性能方面的问题后,又面临了一个新的挑战——渲染一致性。如何在不降低渲染性能的前提下,大幅度提升Flutter与Native之间的渲染一致性呢?

思路

在初版渲染架构设计当中,我们以Widget为中心,采用了组合的方案来完成DSL到Widget的转化。这方面的工作在早期还算比较顺利,然而随着模板复杂度的增加,逐渐出现了一些Bad Case。

分析了这些Bad Case后发现,在初版渲染架构下,无法彻底解决这些Bad Case,原因主要为以下两点:

1. 我们使用了Stack来代表FrameLayout,Column/Row来代表LinearLayout,它们看似功能相似,实则内部实现差异较大,使用过程中引起了很多难以解决的Bad Case。

2. 初版尝试通过自定义Widget对DSL的布局理念做了初步的理解,但是未能做到完全对齐,使得Bad Case无法得到系统性解决。

如需从根本上解决这些问题,需要重新设计一套新的渲染架构方案,完全理解并对齐DSL的布局理念。 

新版渲染架构设计

由于DinamicX的DSL与Android XML十分相似,因此我们将以Android的Measure机制来介绍其布局理念。相信很多同学都明白,在Android的Measure机制中,父View会根据自身的MeasureSpecMode和子View的LayoutParams来计算出子View的MeasureSpecMode,其具体计算表格如下(忽略了MeasureSpecMode为UNSPECIFIED的情况):

我们可以基于上面这个表格,计算出每个DSL Node的宽/高是EXACTLY还是AT_MOST的。Flutter若想理解DynamicX DSL,就需要引入MeasureSpecMode的概念。由于初版渲染架构以Widget为中心,难以引入MeasureSpecMode的概念,因而需要以RenderObject为中心,对渲染架构做重新的设计。

基于RenderObject层,设计了一个新的渲染架构。在新的渲染架构中,每一个DSL Node都会被转化为RenderObject Tree上的一颗子树,这棵子树主要由三部分组成。

  • Decoration层:Decoration层用于支持背景色、边框、圆角、触摸事件等,这些我们可以通过组合方式实现。

  • Render层:Render层用于表达Node在转化后的布局规则与尺寸大小。

  • Content层:Content层负责显示具体内容,对于布局控件来说,内容就是自己的children,而对于非布局控件如TextView、ImageView等,内容将采用Flutter中的RenderParagraph、RenderImage来表达。

Render层为我们新版渲染架构中的核心层,用于表达Node转化后的布局规则与尺寸大小,对于理解DSL布局理念起到了关键性作用,其类图如下:

DXRenderBox是所有控件Render层的基类,其派生了两个类:DXSingleChildLayoutRender和DXMultiChildLayoutRender。其中DXSingleChildLayoutRender是所有非布局控件Render层的基类,而DXMultiChildLayoutRender则是所有布局控件Render层的基类。

对于非布局控件来说,Render层只会影响其尺寸,不影响内部显示的内容,所以理论上View、ImageView、Switch、Checkbox等控件在Render层的表达都是相同的。DXContainerRender就是用于表达这些非布局控件的实现类。这里TextView由于有maxWidth属性会影响其尺寸以及需要特殊处理文字垂直居中的情况,因而单独设计了DXTextContainerRender。

对于布局控件来说,不同的布局控件代表着不同的布局规则,因此不同的布局控件在Render层会派生出不同的实现类。DXLinearLayoutRender和DXFrameLayoutRender分别用于表达LinearLayout与FrameLayout的布局规则。

新版渲染架构实现

完成新版渲染架构设计之后,我们可以开始设计基类DXRenderBox了。对于DXRenderBox来说,我们需要实现它在Flutter Layout中非常关键的三个方法:sizedByParent、performResize和performLayout。

Flutter Layout的原理

我们先来简单回顾一下Flutter Layout的原理,由于之前已有诸多文章介绍过Flutter Layout的原理,这次就直接聚焦于Flutter Layout中用于计算RenderObject的size的部分。

在Flutter Layout的过程中,最为重要的就是确定每个RenderObject的size,而size的确定是在RenderObject的layout方法中完成的。layout方法主要做了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值