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的非同步繪製流程
-
獲取node的displayBlock,也就是負責根據node的檢視層級得到需要顯示的內容的繪製任務。
-
生成Node繪製完成後的回撥completeBlock。
-
根據displaysAsynchronously屬性來判斷是否需要非同步繪製,如果是非同步的,則將displayBlock提交至_ASAsyncTransaction中,否則立即執行displayBlock。
Node的非同步渲染
-
尋找Layer相關的ASAsyncTransaction。
-
將displayBlock和completeBlock新增至ASAsyncTransaction。
-
利用ASAsyncTransactionQueue進行排程。
-
mainRunloop在開始sleep和exit的時候提交ASAsyncTransaction。
-
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原生開發的佈局模式帶來一種新的佈局思維,很新穎,很有特點。