阿里是个重运营的公司,前端开发者居多,2016-2017年,在Weex仍是1.0时代,React Native开源还没多久,Flutter还没诞生的时分,如何在贴合前端开发环境的前提下,快速铺到android/iOS双渠道是个大热点,支付宝内部孵化一个动态化跨渠道计划顺势而生。

前面三篇文章别离介绍了Cube当前架构,Cube卡片和Cube小程序技能产品形状。这篇文章首要评论Cube的烘托规划,协助大家了解Cube卡片烘托技能的宿世此生

Native原生烘托的问题

咱们都知道一个原生view烘托上屏需求几个进程,以android举例:create、measure、layout、draw,这些需求在主线程完结,当完结原生列表时,即便完美复用item,对不同数据烘托时,也需求measure、layout、draw几步缺一不行,并且随着view嵌套层级越深,对主线程资源耗费越大,当列表fly起来以后,帧率快速下降,形成页面卡顿,依据这个问题,cube在调研期间,如何解决烘托功率是重要的一part。

通常来说优化列表翻滚帧率,也便是view层级、布局复杂度、去掉不必要背景色,解决过度制作,图片懒加载、item复用等方面下手,但底子仍是绕不过measure、layout、draw。彼时的weex和RN,也都仍是将html中的标签映射到渠道层view,在某些场景下,开发者又不能像原生开发相同自行优化,在烘托功能上饱尝诟病。因此cube调研期间烘托方针是:优化烘托功率+跨渠道。

跨渠道异步烘托计划

异步烘托

依据上面说到的背景和需求,那么咱们就想,能否有一种方法,把关键进程移除出线程呢,即异步烘托。在列表翻滚时根本只要体系手势和列表本身翻滚算法、动画需求占用主线程,将大大提高帧率。视图内元素制作的产品是一个像素缓存(Cube选用的规划是Bitmap),回到主线程给视图进行改写显现。

Cube 技术解读 | Cube 渲染设计的前世今生

跨渠道架构

另一个方针跨渠道,是要做到能够快速扩展其他渠道,cube将涉及渠道的部分分离出来,形成platform 层。

platform

这儿供给了各渠道通用的规范c++原子接口,在不同渠道用渠道语言完结,初步只完结了android、iOS两个渠道,android经过jni调用java办法,iOS在完结文件中c++、OC混编。假如未来需求扩展其他渠道例如macOS,只需完结platform层界说的接口即可,能够到达快速扩展其他渠道的方针。

core

library是依据platform原子接口用c++完结的是根底库,例如文件IO、UI控件、图片下载、音讯通讯等,供上层引擎运用。library之上,便是cube烘托的中心完结,烘托部分包含数据模型和烘托逻辑,组件库指cube内部支撑的一些体系实体控件,或许开发者可外接的实体组件。 下图是第一版cube烘托架构图。

Cube 技术解读 | Cube 渲染设计的前世今生
cube烘托架构图

异步烘托技能选型

前面说到了,异步烘托计划里异步制作的“产品”是一张bitmap交给“容器”View,为什么是bitmap呢,看起来对内存很不友好,View又是个什么View,有没有特殊性,下面聊聊cube调研时期都研究过哪些计划,终究为什么选型bitmap。

Android渠道技能选型

android的选型之路坎坷高低,最先能想到的支撑独立烘托线程的textureView、GLSurfaceView作为容器,但有显着缺陷,是不能用于常见事务的列表场景的,只能使用于特定场景。

SurfaceView、GLSurfaceView

SurfaceView从android1.0开始就有,首要特点是它的烘托能够在子线程中完结,因此存在的问题是,尽管它继承View,可是它拥有独立的Surface,不在View hierachy中,它的显现也不受View的特点操控,因此不能像一般view相同缩放平移,更不能作为item放在listView/RecycleView中当作一般view运用,翻滚起来会有不同步的问题。

GLSurfaceView继承SurfaceView,它自带GLThread,有和GLSurfaceView相同的问题,总之,这两个view更合适单个视频烘托或许像地图类烘托场景。

有人或许要问,整个页面都用SurfaceView/GLSurfaceView不就行了,连列表也在render线程完结?这儿两个问题:

1、假如列表容器也在render线程完结,正如现在的flutter相同,那么列表滑动手势处理需求自己完结,比方drag,fling,各种列表翻翻滚画,以及翻滚加速度计算等,本钱很高。并且,touch事情捕获依然依靠渠道层,而处理事情需求切换到render线程,这中心必定有线程切换本钱形成的不跟手的体会问题。现在很多依据flutter引擎改造的烘托引擎,正面临着这些问题;

2、在其时cube团队的首要方针是快速验证 ,列表的完结这种本钱过高,不是首要矛盾地点。

Cube 技术解读 | Cube 渲染设计的前世今生

TextureVIew

textureView是google从android4.0开始供给的,它的呈现很大程度上是为了补偿SurfaceView、GLSurfaceView与原生View融合的缺乏,依据上面一节描绘的这两个view与原生view一同动画的问题,textureView好像更合适咱们的场景,既能支撑独立render线程,又能保证与原生view完美融合。

可是,在实践的调研进程中发现,textureView的烘托机制,不适用于长列表,假如每个列表的item是一个textureView,那么就涉及到出屏回收,进屏创立,否则会带来内存问题。而回收和创立SurfaceTexture是异步进程,呈现了闪黑屏问题。除此之外,进一步发现textureView的数量和容量(每个view的尺寸累计)存在某个上限,并且不同手机上限也差异很大。简单说,这是一个看起来很美好,可是兼容性坑无数的技能道路。

Cube 技术解读 | Cube 渲染设计的前世今生

Bitmap+一般View

终究挑选了bitmap看起来并不完美的计划,尽管这被大多数android开发以为bitmap带来大量内存耗费,视为不行承受,但随着cube的使用规模越来越广,这逐步被证明是在其时,最普适的一个计划。

每一个layer对应一个体系view,每个view的制作内容在子线程经过CanvasAPI异步制作在bitmap上,当view上屏时,体系onDraw制作这个bitmap“产品”。

BitmapCache 尽管用了Bitmap制作计划,但必需求考虑内存过载的问题,这儿咱们选用了BitmapCache,首要针对列表类型场景,依靠体系的item回收回调通知,将bitmap画布放入Cache,item上屏烘托时,优先从cache取bitmap画布运用,优先取相同大小的,假如不存在,则取width、height大于方针width、height,让view只制作bitmap局部,到达正确烘托的目的

iOS渠道技能选型

iOS的完结原理与android大致相同,区别是,iOS异步线程制作完结的“产品”,不会在UIView的drawRect里运用CoreGraphics进行烘托,这种方法功率很低,页面卡顿显着,终究选用的是将画布赋值给UIView的layer,托管给体系烘托layer。

烘托技能的演进

上面讲了cube异步烘托大体计划和关键技能选型,事实上,从19年初上线答答星球,到现在,cube在支付宝内使用越来越广泛,这中心也伴随着cube团队依据实践事务场景不断探索、优化的进程,烘托链路经历了两次重构。需求强调的,这个演进进程是在严格的内存/功能下完结的,并且要对Android兼容性做出退让。一些看起来不那么优雅或许先进的规划,事实上是不得不这么做,比方挑选Bitmap作为像素缓冲,比方接入三方组件的规划等。从某种含义上,抛开束缚议论技能好坏也含义不大。咱们从前学习flutter的部分,但Cube终究仍是沿着合适本身场景的技能道路往前走。

常见术语

LayoutTree:DomApi经过add、update、remove构建的经过yoga布局的,用来描绘节点父子关系,包含布局信息的原始树型结构;

RenderTree:用来描制作作节点父子关系,包含制作信息的树型结构,与layoutTree的区别举例:一个layoutNode visible为gone,则该节点不会在RenderTree中呈现;

Layer:一般情况下,根节点及其子节点制作在同一个画布上,界说为一个layer,对应渠道层一个view,当子节点有动画特点,或许超出父节点规模,则需求独立出一个layer;

LayerTree:上面说到的layer节点,构建的树型结构,一个layer对应渠道层一个view,咱们叫ContainerView;

实体节点:需求独立layer的节点为实体节点;

虚拟节点:除了实体节点以外,其他节点均会被制作在父容器的画布上,这些是虚拟节点。

演进进程

1调研初期——1.0验证计划的可行性

调研时期验证计划可行性,场景比较简单,以支付宝内朋友动态页面为验证场景,每条状态(一个item/cell)作为一个烘托单元,这儿只考虑了layerTree只要一个layer的情况,头像、昵称、时刻、配图、“赞”、“赏”,“评”等元素均制作在root节点对应的layer上,“赞”、“赏”,“评”文本周围的小图标则作为外接实体组件,经过addSubView添加在rootLayer的View上。

Cube 技术解读 | Cube 渲染设计的前世今生

数据模型

如下图所示,依据layoutTree构建RenderTree,但非烘托节点不在renderTree上,layerTree只要一个自制作layer(rootLayer),和其他自界说组件X,终究除自界说组件外,其他所有节点都制作在rootLayer上。

Cube 技术解读 | Cube 渲染设计的前世今生

烘托流程

bridge线程经过DomApi构建layoutTree,当主线程触发烘托时,主线程依据layoutTree构建RenderTree,构建进程中遇到外接实体组件,创立实例并addSubView,之后切换子线程制作RenderTree,即rootLayer上的所有虚拟节点,制作完结后切换主线程贴图(bitmap“产品”)。

Cube 技术解读 | Cube 渲染设计的前世今生

缺陷

不能支撑多layer结构

实体view没有复用,也便是朋友动态列表中有多少item/cell,就会有多少“赞”、“赏”,“评”实体组件

但这个调研验证了异步烘托的可行性,在列表翻滚时帧率大幅提高。

2产品化时期——2.0支撑多layer

前面验证了可行性,在进行产品化规划时,就必需求满足多layer结构了,即实践的一张卡片中,会有一个或几个不同的节点被设置为layer,这些节点及其子节点,别离制作在不同画布上,供不同的layer烘托。

数据模型

改善之处时layerTree里有个多layer节点,layer节点下面的子虚拟节点,将制作在该layer的bitmap“产品”上。

Cube 技术解读 | Cube 渲染设计的前世今生

烘托流程

brige线程构建layoutTree的进程中,每个指令(addNode、removeNode……)都会相应分发到render模块的主线程,render依据指令构建RenderTree,并用指令信息生成task入队,当VSync信号来时,触发使命出队并去重,构建layerTree,不同layer分发到不同draw线程制作,制作完结后切主线程贴图(bitmap“产品”)。

Cube 技术解读 | Cube 渲染设计的前世今生

缺陷

主线程计算量大,或许形成卡顿

render节点既包含制作信息,是制作目标,还包含逻辑,例如display:”none”节点疏忽不显现,责任不明晰。

3优化时期——3.0取长补短

上面能够看到renderTree的构建以及layerTree的构建,都是在UI线程,在节点数比较多或复杂的情况下会形成UI的卡顿,为了追求极致翻滚帧率,尽或许削减主线程计算内容,优化3.0版本将renderObject构建layer、以及计算节点变更导致的制作影响规模,的部分改在子线程完结,形成了现在线上运转的版本。

数据模型

新增了PaintTree这个结构,它挂载在Layer节点上,样式和特点值从RenderTree拷贝而来,但不涉及任何逻辑处理,单纯的是一个制作目标,每个制作使命只制作paintTree上的paint节点,与layerTree和renderTree没有并发问题。

Cube 技术解读 | Cube 渲染设计的前世今生

烘托流程

layout线程构建layoutTree,切换到render线程构建renderTree,当渠道层触发烘托,切换到renderTree构建layerTree,并计算影响规模等,切换到主线程将layer对应的实体化View添加在容器View上,生成制作使命在paint线程执行,制作结束后切换主线程贴图(bitmap产品)。

Cube 技术解读 | Cube 渲染设计的前世今生

缺陷

render线程繁忙时形成的闪白率升高

以上便是cube烘托从诞生到现在线上计划的演进,目前在支付宝端内卡片形状接入事务超过20+,线上运转的卡片模版个数到达500多个,显现PV过百亿,经受住了各事务方的考验。

但在技能支撑中也发现了一些问题,例如烘托使命过多时,render线程阻塞排队,不能及时消费导致白屏概率变大,最近cube也在继续研究优化计划。

存在的问题

两端共同性问题

cube目前的制作api,选用的体系渠道层供给的CanvasApi(iOS是CoreGraphics),这就导致了两个渠道在制作点线面的细节上有必要两端人工代码对齐,否则就会发生效果差异,当新增一些feature,例如支撑点划线,需求两个渠道各自完结DrawDottedLine接口,但这个问题,cube团队正调研自制作,即便用skia api将制作接口下沉到c++,完结跨渠道自制作;

文本也是容易发生差异的一个点,运用渠道层api对文本进行布局,在制作时调用布局的api进行制作,因此或许会产品渠道差异,但cube团队目前已经在Cube小程序上把文本布局,布局算法下沉在c++层,不依靠渠道api,完结双渠道共同;限于内存/功能的束缚尚未在Cube卡片上使用。

闪白问题

由于翻滚选用的异步烘托,所以必然会发生主线程卡片已经上屏,异步制作还未完结形成的闪白问题,线程切换有本钱,这个闪白理论上必定存在,仅仅时刻长短问题,cube团队致力于提高烘托功率,将线程切换带来的损耗降到最低,运用户在列表翻滚中体会提高。

未来规划

针对目前已知的问题,cube团队致力于继续优化,首要优化点包含但不限于以下:

烘托快照,提高冷启的烘托功率,削减闪白时刻;

烘托策略,例如预烘托、同异步制作自适应、线程模型优化、组件缓存和预加载等,削减闪白率,提高烘托功率;

用于Cube卡片的yoga布局引擎优化,提高layout布局功率;

skia自制作完结,完结双端共同性;

cube的烘托技能的使用包含卡片和小程序两种技能形状,场景包含支付宝端内、端外、IOT等多样化场景,团队成员将继续在烘托功能、用户体会、以及工具链等方向继续发力,努力把产品打磨好,把开发者服务好,生长为具有竞争力的跨渠道动态化烘托计划。

Cube 技术解读 | Cube 渲染设计的前世今生