38

Texture的异步渲染和布局引擎

 5 years ago
source link: http://www.cocoachina.com/ios/20181019/25248.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

探讨的几个点

  • Texture的简介 (What)

  • 为什么要使用Texture (Why)

  • Texture的作者 (Who)

  • Node的异步绘制如何实现 (How)

  • Node的异步渲染(Runloop任务分发)如何实现 (How)

  • Texture的布局引擎 (How)

  • Texture的使用能带来什么收益 (How Much)

texture简介:

Texture(原名AsyncDisplayKit)是FaceBook开源的一款能够保持界面流畅的框架。建立在UIKit之上,可以保持最复杂的用户界面的流畅和响应。(smooth and responsive)

r2QjQ3R.png!web

Texture整体架构

Rb6zqm6.png!web

Node:对UIView和CALayer的抽象

Node Containers:node容器,负责加载渲染node

Layout Engineer:node布局

Texture节点容器与UIKit

uqUJRrF.png!web

Texture节点子类与UIKit

rU3AnaM.png!web

Texture节点子类继承树

zq22Uzj.png!web

为什么要使用Texture

  • 布局计算、解码、绘制,异步并发执行

  • Runloop任务分发(异步渲染)

  • 声明式布局系统

  • 图层预合成

  • 深度优化列表性能(智能预加载)

Texture:图层预合成

有时一个 layer 会包含很多 sub-layer,而这些 sub-layer 并不需要响应触摸事件,也不需要进行动画和位置调整。ASDK 为此实现了一个被称为 pre-composing 的技术,可以把这些 sub-layer 合成渲染为一张图片。开发时,ASNode 已经替代了 UIView 和 CALayer;直接使用各种 Node 控件并设置为 layer backed 后,ASNode 甚至可以通过预合成来避免创建内部的 UIView 和 CALayer。

通过这种方式,把一个大的层级,通过一个大的绘制方法绘制到一张图上,性能会获得很大提升。CPU 避免了创建 UIKit 对象的资源消耗,GPU 避免了多张 texture 合成和渲染的消耗,更少的 bitmap 也意味着更少的内存占用。

jm2iQ3U.png!webZjmiAr6.png!web

图层预合成

Texture:智能预加载

所有节点都持有当前界面状态interfaceState,由ASRangeController控制属性值更新,在所有节点容器的内部创建和维护。

QVNn22b.png!web

智能预加载

  • Preload:节点还不可见,这时节点收集外部源(API或磁盘数据)

  • Display:节点开始渲染,包括文本的光栅化以及图像解码等

  • Visible:节点可见,在屏幕上至少拥有一个像素

Texture的作者

Scott Goodson

7nM3YvV.png!web

ASDK 的作者是 Scott Goodson ,他曾经在苹果工作,负责 iOS 的一些内置应用的开发,比如股票、计算器、地图、钟表、设置、Safari 等,当然他也参与了 UIKit framework 的开发。后来他加入 Facebook 后,负责 Paper 的开发,创建并开源了 AsyncDisplayKit。目前他在 Pinterest 和 Instagram 负责 iOS 开发和用户体验的提升等工作。

Node的异步绘制如何实现

UIKit的绘制机制图解

CALayer的display方法由系统调用,用来更新layer的内容,如果layer有delegate对象,那么display方法将尝试调用delegate的displayLayer:方法来更新layer的内容。如果delegate没有实现displayLayer:方法,则这个方法会创建一个backing store来保存原来的内容,然后调用layer的drawInContext:方法来填充back store。最后以新的backing store替换layer的先前内容达到更新layer的目的。通常UIKit中CALayer的delegate是UIView对象。

有两种方式来自定义CALayer的内容,一种是直接设置CALayer的contents属性来创建寄宿图;另一种是通过实现CALayer的delegate方法,可以用于直接对CALayer进行操作。

Node的异步绘制

UIKit

ziaMfam.png!web

Texture

qmYzA3j.png!web

ASDisplayNode是整个Texture的基石,也是页面异步绘制的核心,其持有UIView和CALayer两种对象,均由node自己生成并管理。

zUz6nmV.png!web

异步绘制时序图

_ASDisplayLayer通过重写了CALayer的display方法来自定义CALayer的寄宿图属性。_ASDisplayLayer与ASDisplayNode的关系类似于CALayer与UIView的关系。

Node的异步绘制流程

  1. 获取node的displayBlock,也就是负责根据node的视图层级得到需要显示的内容的绘制任务。

  2. 生成Node绘制完成后的回调completeBlock。

  3. 根据displaysAsynchronously属性来判断是否需要异步绘制,如果是异步的,则将displayBlock提交至_ASAsyncTransaction中,否则立即执行displayBlock。

rM73i2y.png!web

Node的异步渲染

  1. 寻找Layer相关的ASAsyncTransaction。

  2. 将displayBlock和completeBlock添加至ASAsyncTransaction。

  3. 利用ASAsyncTransactionQueue进行调度。

  4. mainRunloop在开始sleep和exit的时候提交ASAsyncTransaction。

  5. ASAsyncTransaction在提交的时候回调completeBlock,完成layer寄宿图的赋值。

底层信号驱动原理

jQ3YJjF.png!web

iOS的显示系统由VSync信号驱动的,VSync信号由硬件时钟生成,每秒钟发出60次。iOS图形服务接收到VSync信号后,会通过IPC通知到APP内。APP的Runloop在启动后会注册对应CFRunloopSource通过mach_port接收传过来的时钟信号通知,随后source的回调会驱动整个App的动画与显示。

Runloop任务分发 ->CoreAnimation

CA在Runloop中注册了一个Observer,监听了BeforeWaiting和Exit事件,优先级低于其他Observer。当一个触摸事件到来时,Runloop被唤醒,App中的代码会执行一些操作,比如创建和调整视图层级、设置UIView的frame、修改CALayer的透明度、为视图添加一个动画;这些操作最终会被CALayer捕获,并通过CATransaction提交到一个中间状态去。当上面的所有操作结束后,Runloop即将进入休眠(或者退出)时,关注该事件的Observer都会得到通知。这时CA注册的Observer就会在回调中,把所有的中间状态合并提交到GPU去显示;如果此处有动画,CA会通过CADisplayLink等机制多次触发相关流程。

Runloop任务分发->Texture

Texture在此处模拟了Core Animation的这个机制:所有针对ASNode的修改和提交,总有些任务是必须放入主线程执行的。当出现这种任务时,ASNode会把任务用ASAsyncTransaction(Group)封装并提交到一个全局的容器去。Texture也在Runloop中注册了一个Observer,监视的事件和CA一样,但优先级比CA要低。当Runloop进入休眠前、CA处理完事件后,Texture就会执行该loop内提交的所有任务。通过这种机制,Texture可以在合适的机会把异步、并发的操作同步到主线程去,并且能获得不错的性能。

Texture布局引擎

相对于AutoLayout

UIKit AutoLayout 在复杂的视图结构中,计算量会呈指数级增长,Texture的布局方案相对AutoLayout有以下优点:

  • 快:Texture的布局计算和手写frame一样快

  • 异步和并发:布局可以在后台线程上计算

  • 声明式渲染:布局使用不可变的数据结构声明,实现一个layout视角从专注view之间的距离和约束,转变成划分和制定不同view子域的布局规则,抽象层级变高,使得布局代码更容易开发、维护

  • 可缓存:如果布局是不变的,自动在后台预先计算并缓存

  • 可拓展:在不同的类中使用相同的布局会变得很方便

Texture的布局系统

Texture自己定义了一套强大的automatic layout布局系统,这套布局系统基于CSS的Box Model,通过提出LayoutSpec概念,使得我们可以通过声明式的方法来定义布局。

U3eIj2i.png!web

layoutTable

:一种特殊的layoutTable,与node不同的是,它本质只是内存中的数据结构,用以辅助view的位置计算,绘制时不需要view来占位或者承载子元素。

aUjUvyy.png!web

布局系统

Texture布局规则&布局元素

  • 布局规则:充当LayoutElements的容器,通过多个LayoutElements之间的关联,完成LayoutElements的位置排列,继承自ASLayoutSpec。

  • 布局元素:所有的ASDisplayNode和ASLayoutSpec都遵守 协议,可以通过两个Nodes和其他的LayoutSpecs,生成或者组合一个新的LayoutSpecs。 协议及LayoutSpecs有一些属性用于创建非常复杂的布局。

MBvURnZ.png!web

Texture布局流程图

IBBBfiR.png!web

Texture布局示例

2aUF7n7.png!web

Texture布局调试

在任何ASDisplayNode或ASLayoutSpec上调用-asciiArtString都会返回该对象及其子项的字符图,也可以设置.debugName这样也会包含在字符图中。

nU7N7fV.png!web

还可以在任何ASLayoutElement,比如Node和LayoutSpec上打印样式对象,调试.size属性。

mQRRbiR.png!web

声明式布局Demo链接:

https://[email protected]/ysw-hello/TextureLayoutDemo.git

Texture所能带来的收益

  • 异步绘制、异步渲染通过Runloop任务分发,优化复杂界面的主线程卡顿现象。

  • 图层预合成、智能预加载的机制,对列表进行深度优化,使得体验与性能得到进一步的提升。

  • 声明式布局方式,FlexBox布局特点,给iOS原生开发的布局模式带来一种新的布局思维,很新颖,很有特点。

作者:SuperBoy_Timmy

链接:https://www.jianshu.com/p/1186a99dded5


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK