1. 程式人生 > >Texture的非同步渲染和佈局引擎

Texture的非同步渲染和佈局引擎

  • Texture的簡介 (What)

  • 為什麼要使用Texture (Why)

  • Texture的作者 (Who)

  • Node的非同步繪製如何實現 (How)

  • Node的非同步渲染(Runloop任務分發)如何實現 (How)

  • Texture的佈局引擎 (How)

  • Texture的使用能帶來什麼收益 (How Much)

texture簡介:

Texture(原名AsyncDisplayKit)是FaceBook開源的一款能夠保持介面流暢的框架。建立在UIKit之上,可以保持最複雜的使用者介面的流暢和響應。(smooth and responsive)

Texture整體架構

Node:對UIView和CALayer的抽象

Node Containers:node容器,負責載入渲染node

Layout Engineer:node佈局

Texture節點容器與UIKit

Texture節點子類與UIKit

Texture節點子類繼承樹

為什麼要使用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 也意味著更少的記憶體佔用。

圖層預合成

Texture:智慧預載入

所有節點都持有當前介面狀態interfaceState,由ASRangeController控制屬性值更新,在所有節點容器的內部建立和維護。

智慧預載入

  • Preload:節點還不可見,這時節點收集外部源(API或磁碟資料)

  • Display:節點開始渲染,包括文字的光柵化以及影象解碼等

  • Visible:節點可見,在螢幕上至少擁有一個畫素

Texture的作者

Scott Goodson

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

Texture

ASDisplayNode是整個Texture的基石,也是頁面非同步繪製的核心,其持有UIView和CALayer兩種物件,均由node自己生成並管理。

非同步繪製時序圖

 

_ASDisplayLayer通過重寫了CALayer的display方法來自定義CALayer的寄宿圖屬性。_ASDisplayLayer與ASDisplayNode的關係類似於CALayer與UIView的關係。

Node的非同步繪製流程

  1. 獲取node的displayBlock,也就是負責根據node的檢視層級得到需要顯示的內容的繪製任務。

  2. 生成Node繪製完成後的回撥completeBlock。

  3. 根據displaysAsynchronously屬性來判斷是否需要非同步繪製,如果是非同步的,則將displayBlock提交至_ASAsyncTransaction中,否則立即執行displayBlock。

Node的非同步渲染

  1. 尋找Layer相關的ASAsyncTransaction。

  2. 將displayBlock和completeBlock新增至ASAsyncTransaction。

  3. 利用ASAsyncTransactionQueue進行排程。

  4. mainRunloop在開始sleep和exit的時候提交ASAsyncTransaction。

  5. ASAsyncTransaction在提交的時候回撥completeBlock,完成layer寄宿圖的賦值。

<ps:displayblock執行不在主執行緒,completeblock執行在主執行緒!>

底層訊號驅動原理

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概念,使得我們可以通過宣告式的方法來定義佈局。

layoutTable

:一種特殊的layoutTable,與node不同的是,它本質只是記憶體中的資料結構,用以輔助view的位置計算,繪製時不需要view來佔位或者承載子元素。

佈局系統

Texture佈局規則&佈局元素

  • 佈局規則:充當LayoutElements的容器,通過多個LayoutElements之間的關聯,完成LayoutElements的位置排列,繼承自ASLayoutSpec。

  • 佈局元素:所有的ASDisplayNode和ASLayoutSpec都遵守協議,可以通過兩個Nodes和其他的LayoutSpecs,生成或者組合一個新的LayoutSpecs。協議及LayoutSpecs有一些屬性用於建立非常複雜的佈局。

Texture佈局流程圖

Texture佈局示例

Texture佈局除錯

在任何ASDisplayNode或ASLayoutSpec上呼叫-asciiArtString都會返回該物件及其子項的字元圖,也可以設定.debugName這樣也會包含在字元圖中。

還可以在任何ASLayoutElement,比如Node和LayoutSpec上列印樣式物件,除錯.size屬性。

宣告式佈局Demo連結:

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

Texture所能帶來的收益

  • 非同步繪製、非同步渲染通過Runloop任務分發,優化複雜介面的主執行緒卡頓現象。

  • 圖層預合成、智慧預載入的機制,對列表進行深度優化,使得體驗與效能得到進一步的提升。

  • 宣告式佈局方式,FlexBox佈局特點,給iOS原生開發的佈局模式帶來一種新的佈局思維,很新穎,很有特點。