萬(wàn)字深入HarmonyOS ACE UI框架解析,帶你看懂UI渲染流程

2021-08-04 14:45:15來(lái)源:威易網(wǎng)作者:

UI 框架簡(jiǎn)介以及業(yè)界發(fā)展趨勢(shì)
UI,即用戶(hù)界面,主要包含視覺(jué)(比如圖像、文字、動(dòng)畫(huà)等可視化內(nèi)容)以及交互(比如按鈕點(diǎn)擊、列表滑動(dòng)、圖片縮放等用戶(hù)操作)。UI框架,則是為開(kāi)發(fā)UI而提供的基礎(chǔ)設(shè)施,比如視圖布局,U

UI 框架簡(jiǎn)介以及業(yè)界發(fā)展趨勢(shì)

UI,即用戶(hù)界面,主要包含視覺(jué)(比如圖像、文字、動(dòng)畫(huà)等可視化內(nèi)容)以及交互(比如按鈕點(diǎn)擊、列表滑動(dòng)、圖片縮放等用戶(hù)操作)。UI框架,則是為開(kāi)發(fā)UI而提供的基礎(chǔ)設(shè)施,比如視圖布局,UI組件,事件響應(yīng)機(jī)制等。

從操作系統(tǒng)平臺(tái)支持方式來(lái)看,UI框架一般可分為原生UI框架和跨平臺(tái)UI框架兩種。

1.原生UI框架。這個(gè)一般是指操作系統(tǒng)自帶的UI框架,典型的例子包括iOS的UI Kit,Android的View框架等。這些UI框架和操作系統(tǒng)深度綁定,一般只能運(yùn)行在相應(yīng)的操作系統(tǒng)上。功能,性能,開(kāi)發(fā)調(diào)測(cè)等方面和相應(yīng)的操作系統(tǒng)結(jié)合較好。

2.跨平臺(tái)UI框架。這個(gè)一般是指可以在不同的平臺(tái)(OS)上運(yùn)行的獨(dú)立的UI框架。典型例子包括HTML5以及基于HTML5延伸出來(lái)的前端框架React Native, 以及Google 的Flutter等。 跨平臺(tái)UI框架的目標(biāo)是代碼只需一次編寫(xiě),經(jīng)過(guò)少量修改甚至不修改,可以部署到不同的操作系統(tǒng)平臺(tái)上。當(dāng)然,實(shí)現(xiàn)跨平臺(tái)也是有代價(jià)的,由于不同平臺(tái)存在差異性(比如UI的呈現(xiàn)方式差異,API差異等等),導(dǎo)致UI框架本身的架構(gòu)實(shí)現(xiàn),以及和不同平臺(tái)的融合都有不小的挑戰(zhàn)。

從編程方式上來(lái)看,UI框架一般可分為命令式UI框架和聲明式UI框架兩種:

1.命令式UI框架。過(guò)程導(dǎo)向 - 告訴“機(jī)器”具體步驟,命令“機(jī)器”按照指定步驟去做。比如Android原生UI框架(View框架)或iOS的UIKit,提供了一系列的API讓開(kāi)發(fā)者直接操控UI組件-比如定位到某個(gè)指定UI組件,進(jìn)行屬性變更等。這種方式的優(yōu)點(diǎn)是開(kāi)發(fā)者可以控制具體的實(shí)現(xiàn)路徑,經(jīng)驗(yàn)豐富的開(kāi)發(fā)者能夠?qū)懗鲚^為高效的實(shí)現(xiàn)。不過(guò)這種情況下,開(kāi)發(fā)者需了解大量的API細(xì)節(jié)并指定好具體的執(zhí)行路徑,開(kāi)發(fā)門(mén)檻較高。具體的實(shí)現(xiàn)效果上,也高度依賴(lài)開(kāi)發(fā)者本身的開(kāi)發(fā)技能。另外,由于和具體實(shí)現(xiàn)綁定較緊,在跨設(shè)備情況下,靈活性和擴(kuò)展性相對(duì)有限。

2.聲明式UI框架。結(jié)果導(dǎo)向 - 告訴“機(jī)器”你需要什么,機(jī)器負(fù)責(zé)怎么去做。比如Web前端框架Vue,或iOS的SwiftUI等,框架會(huì)根據(jù)聲明式語(yǔ)法的描述,渲染出相應(yīng)的UI,同時(shí)結(jié)合相應(yīng)編程模型,框架會(huì)根據(jù)數(shù)據(jù)的變化來(lái)自動(dòng)更新相應(yīng)的UI。

這種方式的優(yōu)點(diǎn)是開(kāi)發(fā)者只需描述好結(jié)果,相應(yīng)的實(shí)現(xiàn)和優(yōu)化由框架來(lái)處理。另外,由于結(jié)果描述和具體實(shí)現(xiàn)分離,實(shí)現(xiàn)方式相對(duì)靈活同時(shí)容易擴(kuò)展。不過(guò)這種情況下,對(duì)框架的要求較高,需要框架有完備的直觀的描述能力并能夠針對(duì)相應(yīng)的描述信息實(shí)現(xiàn)高效的處理。

UI框架是應(yīng)用開(kāi)發(fā)的核心組成部分。縱觀業(yè)界UI框架,其主要發(fā)展趨勢(shì)表現(xiàn)為:

1.從命令式UI往聲明式UI發(fā)展

比如iOS中的UIKit到SwiftUI, Android中的View到Jetpack Compose。

這樣可以實(shí)現(xiàn)更加直觀便捷的UI開(kāi)發(fā)。

2.UI框架和語(yǔ)言運(yùn)行時(shí)深度融合

SwiftUI,Jetpack Compose, Flutter都利用了各自的語(yǔ)言特性 - 比如在UI描述方面,SwiftUI中的Swift語(yǔ)言,Jetpack Compose中的Kotlin語(yǔ)言都精簡(jiǎn)了UI描述語(yǔ)法;在性能方面, Swift通過(guò)引入輕量化結(jié)構(gòu)體等語(yǔ)言特性更好的實(shí)現(xiàn)內(nèi)存快速分配和釋放,F(xiàn)lutter中Dart語(yǔ)言則在運(yùn)行時(shí)專(zhuān)門(mén)針對(duì)小對(duì)象內(nèi)存管理做相應(yīng)優(yōu)化等。

3.跨平臺(tái)(OS)能力

跨平臺(tái)(OS)能力可以讓一套代碼復(fù)用到不同的OS上,主要是為了提升開(kāi)發(fā)效率,降低開(kāi)發(fā)成本。不過(guò)這里面也有一系列的挑戰(zhàn),比如運(yùn)行在不同平臺(tái)上的性能問(wèn)題,能力和渲染效果的一致性問(wèn)題等。業(yè)界在這方面也是不斷的演進(jìn),主要有幾種方式:

1.JS/Web方案,比如HTML5利用JS/Web的標(biāo)準(zhǔn)化生態(tài),通過(guò)相應(yīng)的Web引擎實(shí)現(xiàn)跨平臺(tái)目標(biāo);

2.JS+Native混合方式,比如React Native、Weex等,結(jié)合JS橋接到原生UI組件的方式實(shí)現(xiàn)了一套應(yīng)用代碼能夠運(yùn)行到不同OS上;

3.平臺(tái)無(wú)關(guān)的UI自繪制能力+新的語(yǔ)言,比如Flutter,整個(gè)UI基于底層畫(huà)布由框架層來(lái)繪制,同時(shí)結(jié)合Dart語(yǔ)言實(shí)現(xiàn)完整的UI框架。Flutter從設(shè)計(jì)之初就是將跨平臺(tái)能力作為重要的競(jìng)爭(zhēng)力去吸引更多的開(kāi)發(fā)者。

另外,有趣的是,部分原生開(kāi)發(fā)框架也開(kāi)始往跨平臺(tái)演進(jìn)。比如,Android原生的開(kāi)發(fā)框架Jetpack Compose也開(kāi)始將跨OS支持作為其中的目標(biāo),計(jì)劃將Compose拓展到桌面平臺(tái),比如Windows,MacOS等。

此外,隨著智能設(shè)備的普及,多設(shè)備場(chǎng)景下,設(shè)備的形態(tài)差異(屏幕大小、分辨率,形狀, 交互模式等),以及設(shè)備的能力差異(從百K級(jí)內(nèi)存到G級(jí)內(nèi)存設(shè)備等),以及應(yīng)用需要在不同設(shè)備間協(xié)同,這些都對(duì)UI框架以及應(yīng)用開(kāi)發(fā)帶來(lái)了新的挑戰(zhàn)。

ACE UI框架是什么

ACE全稱(chēng)是Ability Cross-platform Environment (元能力跨平臺(tái)執(zhí)行環(huán)境)。是華為設(shè)計(jì)的應(yīng)用在HarmonyOS上的UI框架。ACE UI框架結(jié)合HarmonyOS的基礎(chǔ)運(yùn)行單元Ability,語(yǔ)言和運(yùn)行時(shí),以及各種平臺(tái)(OS)能力API等共同構(gòu)成HarmonyOS應(yīng)用開(kāi)發(fā)的基礎(chǔ),實(shí)現(xiàn)了跨設(shè)備分布式調(diào)度,以及原子化服務(wù)免安裝等能力。

ACE提供兩種開(kāi)發(fā)語(yǔ)言以供不同開(kāi)發(fā)者進(jìn)行選擇,分別為Java語(yǔ)言和JavaScript語(yǔ)言,其中Java僅支持在內(nèi)存較大的設(shè)備上使用如大屏、手機(jī)、平板等設(shè)備使用,而JavaScript支持在百K級(jí)到G級(jí)設(shè)備上使用。

在多設(shè)備場(chǎng)景下,由于不同的設(shè)備形態(tài)以及設(shè)備能力的巨大差異,目前業(yè)界還沒(méi)有任何一個(gè)UI框架能夠較好的解決相應(yīng)的問(wèn)題。

所以ACE UI框架的整體設(shè)計(jì)思路是:

  1. 建立分層機(jī)制,引入高效的UI基礎(chǔ)后端,并能夠與OS平臺(tái)解耦,形成一致化的UI體驗(yàn)
  2. 通過(guò)多前端的方式擴(kuò)展應(yīng)用生態(tài),并結(jié)合聲明式UI,在開(kāi)發(fā)效率上持續(xù)演進(jìn)
  3. 框架層統(tǒng)一結(jié)合語(yǔ)言以及運(yùn)行時(shí),分布式,組件化設(shè)計(jì)等,圍繞跨設(shè)備,進(jìn)一步提升體驗(yàn)

ACE將應(yīng)用的UI界面進(jìn)行解析,通過(guò)創(chuàng)建后端具體UI組件、進(jìn)行布局計(jì)算、資源加載等處理后生成具體繪制指令,并將繪制命令發(fā)送給渲染引擎,渲染引擎將繪制指令轉(zhuǎn)換為具體屏幕像素,最終通過(guò)顯示設(shè)備將應(yīng)用程序轉(zhuǎn)換為可見(jiàn)的界面效果展示給用戶(hù)。

ACE UI框架的整體架構(gòu)如下圖所示,主要由前端框架層、橋接層、引擎層和平臺(tái)抽象層四大部分組成,下面我們一一介紹。

\

1.前端框架層

該層主要包括相應(yīng)的開(kāi)發(fā)范式(比如主流的類(lèi)Web開(kāi)發(fā)范式),組件/API,以及編程模型MVVM(Model-View-ViewModel)。其中Model是數(shù)據(jù)模型層,代表了從數(shù)據(jù)源讀取到的數(shù)據(jù);View是視圖UI層,通過(guò)一定的形式把系統(tǒng)中的數(shù)據(jù)向用戶(hù)呈現(xiàn)出來(lái);ViewModel: 視圖模型層,是數(shù)據(jù)和視圖之間的橋梁。它雙向綁定了視圖和數(shù)據(jù),使得數(shù)據(jù)的變更能夠及時(shí)在視圖上呈現(xiàn),用戶(hù)在視圖上的修改也能夠及時(shí)傳遞給后臺(tái)數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)的UI自動(dòng)變更。

開(kāi)發(fā)范式可以擴(kuò)展,來(lái)支持生態(tài)發(fā)展。不同的開(kāi)發(fā)范式可以統(tǒng)一適配到底層的引擎層

2.橋接層

該層主要是作為一個(gè)中間層,實(shí)現(xiàn)前端開(kāi)發(fā)范式到底層引擎(包括UI后端,語(yǔ)言&運(yùn)行時(shí))的對(duì)接

3.引擎層

該層主要包含兩部分:UI后端引擎和語(yǔ)言執(zhí)行引擎。

1.由C++構(gòu)建的UI后端引擎,包括UI組件、布局視圖、動(dòng)畫(huà)事件、自繪制渲染管線(xiàn)和渲染引擎 。

在渲染方面,我們盡可能把這部分組件設(shè)計(jì)得小而靈活。這樣的設(shè)計(jì),為不同前端框架提供靈活的UI能力,這部分通過(guò)C++組件組合而成。通過(guò)底層組件的按需組合,布局計(jì)算和渲染并行化,并結(jié)合上層開(kāi)發(fā)范式實(shí)現(xiàn)了視圖變化最小化的局部更新機(jī)制,從而實(shí)現(xiàn)高效的UI渲染。

除此之外,引擎層還提供了組件的渲染管線(xiàn)、動(dòng)畫(huà)、主題、事件處理等基礎(chǔ)能力。目前復(fù)用了Flutter引擎提供基礎(chǔ)的圖形渲染能力、字體管理、文字排版等能力,底層使用Skia或其他圖形庫(kù)實(shí)現(xiàn),并通過(guò)OpenGL實(shí)現(xiàn)GPU硬件渲染加速

在多設(shè)備UI適配方面,通過(guò)多種原子化布局能力(自動(dòng)折行、隱藏、等比縮放等),多態(tài)UI控件(描述統(tǒng)一,表現(xiàn)形式多樣),以及統(tǒng)一交互框架(不同的交互方式歸一到統(tǒng)一的事件處理)來(lái)滿(mǎn)足不同設(shè)備的形態(tài)差異化需求。

另外,引擎層也包含了能力擴(kuò)展基礎(chǔ)設(shè)施,來(lái)實(shí)現(xiàn)自定義組件以及系統(tǒng)API的能力擴(kuò)展

2.語(yǔ)言&運(yùn)行時(shí)執(zhí)行引擎?筛鶕(jù)需要切換到不同的運(yùn)行時(shí)執(zhí)行引擎,滿(mǎn)足不同設(shè)備的能力差異化需求

4.平臺(tái)抽象層

該層主要是通過(guò)平臺(tái)抽象,將平臺(tái)依賴(lài)聚焦到底層畫(huà)布,通用線(xiàn)程以及事件機(jī)制等少數(shù)必要的接口上,為跨平臺(tái)打造了相應(yīng)的基礎(chǔ)設(shè)施,并能夠?qū)崿F(xiàn)一致化UI渲染體驗(yàn)。

相應(yīng)的,配套的開(kāi)發(fā)者工具(HUAWEI DevEco Studio)結(jié)合ACE UI的跨平臺(tái)渲染基礎(chǔ)設(shè)施,以及自適應(yīng)渲染,可做到和設(shè)備比較一致的渲染體驗(yàn)以及多設(shè)備上的UI實(shí)時(shí)預(yù)覽。

另外,ACE UI框架還設(shè)計(jì)了可伸縮的架構(gòu),即前端框架、語(yǔ)言運(yùn)行時(shí)、UI后端等都做了解耦,可以有不同的實(shí)現(xiàn)。這樣就具備可部署到百K級(jí)內(nèi)存的輕量級(jí)設(shè)備的能力,如下所示:

\

在ACE UI的輕量化實(shí)現(xiàn)中,通過(guò)前端框架核心下沉C++化,減小JS部分的內(nèi)存占用,使用C++進(jìn)行更為嚴(yán)格的內(nèi)存分配與管理,并且采用更為輕量的JS引擎,UI部分采用輕量的UIKit并結(jié)合輕量圖形引擎,達(dá)到內(nèi)存非常輕量占用的目標(biāo)。接口能力保證是全量能力的子集,這樣可以保證輕量化設(shè)備上可執(zhí)行的應(yīng)用,可以在更高等級(jí)的設(shè)備上執(zhí)行,而無(wú)需重新開(kāi)發(fā)。這也就是采用ACE JS開(kāi)發(fā)范式的優(yōu)勢(shì)所在,采用統(tǒng)一的開(kāi)發(fā)范式進(jìn)行應(yīng)用開(kāi)發(fā)后,開(kāi)發(fā)者無(wú)需關(guān)心具體運(yùn)行時(shí)的前端框架、JS引擎與后端UI組件,根據(jù)運(yùn)行平臺(tái)不同,采用最佳的模塊,保障了應(yīng)用在不同平臺(tái)都可具有最佳的運(yùn)行性能。不過(guò)由于輕量級(jí)設(shè)備上的資源限制, 所支持的API 能力相對(duì)有限,但公共部分的API是完全共通的。

綜上所述,ACE UI框架具備如下特點(diǎn):

  1. 支持主流的語(yǔ)言生態(tài) – JavaScript
  2. 支持類(lèi)Web開(kāi)發(fā)范式, MVVM機(jī)制。并在架構(gòu)上可支持多前端開(kāi)發(fā)范式,進(jìn)一步簡(jiǎn)化開(kāi)發(fā)
  3. 通過(guò)統(tǒng)一的UI后端,實(shí)現(xiàn)高性能以及跨平臺(tái)一致化的渲染體驗(yàn)
  4. 通過(guò)多態(tài)UI、原子化布局、統(tǒng)一交互,以及可伸縮的運(yùn)行時(shí)設(shè)計(jì),進(jìn)一步降低不同設(shè)備形態(tài)下的UI開(kāi)發(fā)門(mén)檻,并能夠通過(guò)統(tǒng)一的開(kāi)發(fā)范式,實(shí)現(xiàn)一套代碼跨設(shè)備部署(覆蓋百K級(jí)到G級(jí)內(nèi)存設(shè)備)

ACE UI框架渲染流程解析

接下來(lái)我們通過(guò)一個(gè)手機(jī)側(cè)ACE JS應(yīng)用渲染流程的完整流程來(lái)介紹ACE UI框架的具體渲染技術(shù)。

1)線(xiàn)程模型

ACE JS應(yīng)用啟動(dòng)時(shí)會(huì)創(chuàng)建一系列線(xiàn)程,形成獨(dú)立的線(xiàn)程模型,以實(shí)現(xiàn)高性能的渲染流程。

每個(gè)ACE JS應(yīng)用的進(jìn)程,包含唯一一個(gè)Platform線(xiàn)程和若干后臺(tái)線(xiàn)程組成的異步任務(wù)線(xiàn)程池:

  • Platform線(xiàn)程:當(dāng)前平臺(tái)的主線(xiàn)程,也就是應(yīng)用的主線(xiàn)程,主要負(fù)責(zé)平臺(tái)層的交互、應(yīng)用生命周期以及窗口環(huán)境的創(chuàng)建
  • 后臺(tái)線(xiàn)程池:一系列后臺(tái)任務(wù),用于一些低優(yōu)先級(jí)的可并行異步任務(wù),如網(wǎng)絡(luò)請(qǐng)求、Asset資源加載等。除此之外,每個(gè)實(shí)例還包括一系列專(zhuān)有線(xiàn)程
  • JS線(xiàn)程:JS前端框架的執(zhí)行線(xiàn)程,應(yīng)用的JS邏輯以及應(yīng)用UI界面的解析構(gòu)建都在該線(xiàn)程執(zhí)行
  • UI線(xiàn)程:引擎的核心線(xiàn)程,組件樹(shù)的構(gòu)建以及整個(gè)渲染管線(xiàn)的核心邏輯都在該線(xiàn)程:包括渲染樹(shù)的構(gòu)建、布局、繪制以及動(dòng)畫(huà)調(diào)度
  • GPU線(xiàn)程:現(xiàn)代的渲染引擎,為了充分發(fā)揮硬件性能,都支持GPU硬件加速,在該線(xiàn)程上,會(huì)通過(guò)系統(tǒng)的窗口句柄,創(chuàng)建GPU加速的OpenGL環(huán)境,負(fù)責(zé)將整個(gè)渲染樹(shù)的內(nèi)容光柵化,直接將每一幀的內(nèi)容渲染合成到該窗口的Surface上并送顯
  • IO線(xiàn)程:主要為了異步的文件IO讀寫(xiě),同時(shí)該線(xiàn)程會(huì)創(chuàng)建一個(gè)離屏的GL環(huán)境,這個(gè)環(huán)境和 GPU線(xiàn)程的GL環(huán)境是同一個(gè)共享組,可以共享資源,圖片資源解碼的內(nèi)容可直接在該線(xiàn)程上傳生成GPU紋理,實(shí)現(xiàn)更高效的圖片渲染

每個(gè)線(xiàn)程的作用,在后續(xù)的渲染流程中還會(huì)進(jìn)一步提到。

2)前端腳本解析

ACE UI框架支持不同的開(kāi)發(fā)范式,可以對(duì)接到不同的前端框架上。

以類(lèi)Web開(kāi)發(fā)范式為例,開(kāi)發(fā)者開(kāi)發(fā)的應(yīng)用,通過(guò)開(kāi)發(fā)工具鏈的編譯,會(huì)生成引擎可執(zhí)行的Bundle文件。應(yīng)用啟動(dòng)時(shí),會(huì)將Bundle文件在JS線(xiàn)程上進(jìn)行加載,并且將該內(nèi)容作為輸入,供JS引擎進(jìn)行解析執(zhí)行,最終生成前端組件的結(jié)構(gòu)化描述,并建立數(shù)據(jù)綁定關(guān)系。例如包含若干簡(jiǎn)單文本的應(yīng)用會(huì)生成類(lèi)似下圖的樹(shù)形結(jié)構(gòu),每個(gè)組件節(jié)點(diǎn)會(huì)包含該節(jié)點(diǎn)的屬性及樣式信息。

\

3)渲染管線(xiàn)構(gòu)建

\

如上圖,前端框架的解析后,根據(jù)具體的組件規(guī)范定義向前端框架對(duì)接層請(qǐng)求創(chuàng)建ACE渲染引擎提供的組件。

前端框架對(duì)接層通過(guò)ACE引擎層提供的Component組件實(shí)現(xiàn)前端組件定義的能力。Component是一個(gè)由C++實(shí)現(xiàn)的UI組件的聲明式描述,描述了UI組件的屬性及樣式,用于生成組件的實(shí)體元素。每一個(gè)前端組件會(huì)對(duì)接到一個(gè)Composed Component,表示一個(gè)組合型的UI組件,通過(guò)不同的子Component組合,構(gòu)造出前端對(duì)應(yīng)的Composed組件。每個(gè)Composed組件是前后端對(duì)接的一個(gè)基礎(chǔ)的更新單位。

以上面的前端組件樹(shù)為例,每個(gè)節(jié)點(diǎn)會(huì)使用一組Composed組件進(jìn)行組合描述,對(duì)應(yīng)關(guān)系如下圖,該對(duì)應(yīng)關(guān)系只是一個(gè)示例,實(shí)際場(chǎng)景的對(duì)應(yīng)關(guān)系可能會(huì)更復(fù)雜。

\

有了每個(gè)前端節(jié)點(diǎn)對(duì)應(yīng)的Component,就形成了一個(gè)完成Page的描述結(jié)構(gòu),通知渲染管線(xiàn)掛載新的頁(yè)面。

在Page掛載之前,渲染管線(xiàn)已經(jīng)提前創(chuàng)建了幾個(gè)關(guān)鍵的核心結(jié)構(gòu),Element樹(shù)和Render樹(shù):

Element樹(shù),Element是Component的實(shí)例,表示一個(gè)具體的組件節(jié)點(diǎn),它形成的Element樹(shù)負(fù)責(zé)維持界面在整個(gè)運(yùn)行時(shí)的樹(shù)形結(jié)構(gòu),方便計(jì)算局部更新算法。另外對(duì)于一些復(fù)雜的組件,在該數(shù)據(jù)結(jié)構(gòu)上會(huì)實(shí)現(xiàn)一些對(duì)子組件邏輯上的管理。

Render樹(shù),對(duì)于每個(gè)可顯示的Element都會(huì)為其創(chuàng)建對(duì)應(yīng)的RenderNode,它負(fù)責(zé)一個(gè)節(jié)點(diǎn)的顯示信息,它形成的Render樹(shù)維護(hù)著整個(gè)界面的渲染需要用到的信息,包括它的位置、大小、繪制命令等,界面后續(xù)的布局、繪制都是在Render樹(shù)上進(jìn)行的。

當(dāng)應(yīng)用啟動(dòng)時(shí),最初形成的Element樹(shù)只有幾個(gè)基礎(chǔ)的幾節(jié)點(diǎn),一般包括root、overlay、stage,分別作用如下:

RootElement:Element樹(shù)的根節(jié)點(diǎn),僅僅負(fù)責(zé)全局背景色的繪制

OverlayElement:一個(gè)全局的懸浮層容器,用于彈窗等全局繪制場(chǎng)景的管理

StageElement:一個(gè)Stack容器,作為全局的“舞臺(tái)”,每個(gè)加載完成的頁(yè)面都要掛載到這個(gè)“舞臺(tái)”下,它管理應(yīng)用的多個(gè)頁(yè)面之間的轉(zhuǎn)場(chǎng)動(dòng)效等。

在Element樹(shù)創(chuàng)建的過(guò)程中,也會(huì)同步的把Render樹(shù)也創(chuàng)建起來(lái),初始狀態(tài)如下圖:

\

當(dāng)前端框架對(duì)接層通知渲染管線(xiàn)準(zhǔn)備好了頁(yè)面,在下一個(gè)幀同步信號(hào)(VSync)到來(lái)時(shí),就會(huì)在渲染管線(xiàn)上進(jìn)行頁(yè)面的掛載,具體流程就是通過(guò)Component來(lái)實(shí)例化生成Element的過(guò)程,創(chuàng)建成功的Element同步創(chuàng)建對(duì)應(yīng)的RenderNode:

\

如上圖所示,目標(biāo)要將整個(gè)Page的Component描述掛載到StageElement上,如果當(dāng)前Stage下還未有任何Element節(jié)點(diǎn),就會(huì)遞歸逐個(gè)節(jié)點(diǎn)生成Component對(duì)應(yīng)的Element節(jié)點(diǎn)。對(duì)于組合類(lèi)型的ComposedElement,則同時(shí)會(huì)把Element的引用記錄到一個(gè)Composed Map中,方便后續(xù)更新時(shí)快速查找。對(duì)于可見(jiàn)類(lèi)型的容器節(jié)點(diǎn)或渲染節(jié)點(diǎn),則會(huì)創(chuàng)建對(duì)應(yīng)的RenderNode,并掛在Render樹(shù)上。

當(dāng)生成了當(dāng)前頁(yè)面的Element樹(shù)和Render樹(shù),頁(yè)面渲染構(gòu)建的完整過(guò)程就結(jié)束了。

4)布局繪制機(jī)制

接下來(lái)就進(jìn)入了布局和繪制的階段,布局和繪制都是在Render樹(shù)上進(jìn)行的。每個(gè)RenderNode都會(huì)實(shí)現(xiàn)自己的布局算法和繪制方法。

布局

布局的過(guò)程就是通過(guò)各類(lèi)布局的算法計(jì)算出每個(gè)RenderNode在相對(duì)空間上的真實(shí)大小和位置。

如下圖所示,當(dāng)某個(gè)節(jié)點(diǎn)的內(nèi)容發(fā)生變化時(shí),就會(huì)標(biāo)記自己為needLayout,并一直向上標(biāo)記到布局邊界(ReLayout Boundary),布局邊界是重新布局的一個(gè)范圍標(biāo)記,一般情況下,如果一個(gè)節(jié)點(diǎn)的布局參數(shù)信息(LayoutParam)是強(qiáng)約束的,例如它布局期望的最大尺寸和最小尺寸是相同的,那么它就可以作為一個(gè)布局邊界。布局是個(gè)深度優(yōu)先遍歷的過(guò)程。從布局邊界開(kāi)始,父節(jié)點(diǎn)自頂向下將LayoutParam傳給子節(jié)點(diǎn),子節(jié)點(diǎn)自底向上據(jù)此計(jì)算得到尺寸大小和位置,

\

對(duì)于每個(gè)節(jié)點(diǎn)來(lái)說(shuō),布局分為三個(gè)步驟:

  1. 當(dāng)前節(jié)點(diǎn)遞歸調(diào)用子節(jié)點(diǎn)的layout方法,并傳遞布局的參數(shù)信息(LayoutParam),包含了布局期望的最大尺寸和最小尺寸等
  2. 子節(jié)點(diǎn)根據(jù)布局參數(shù)信息,使用自己定義的布局算法來(lái)計(jì)算自己的尺寸大小
  3. 當(dāng)前節(jié)點(diǎn)獲取子節(jié)點(diǎn)布局后的大小,再根據(jù)自己的布局算法來(lái)計(jì)算每個(gè)子節(jié)點(diǎn)的位置信息,并將相對(duì)位置設(shè)置給子節(jié)點(diǎn)保存

根據(jù)上述的流程,一次布局遍歷完成后,每個(gè)節(jié)點(diǎn)的大小和位置就都計(jì)算出來(lái)了,可以進(jìn)行下一步的繪制。

繪制

同布局一樣,繪制也是一個(gè)深度遍歷的過(guò)程,遍歷調(diào)用每個(gè)RenderNode的Paint方法,此時(shí)的繪制只是根據(jù)布局算出來(lái)的大小和位置,在當(dāng)前繪制的上下文記錄每個(gè)節(jié)點(diǎn)的繪制命令。

為什么是記錄命令,而不是直接繪制渲染呢?在現(xiàn)代的渲染引擎中,為了充分使用GPU硬件加速的能力,一般都會(huì)使用DisplayList的機(jī)制,繪制過(guò)程中僅僅將繪制的命令記錄下來(lái),在GPU渲染的時(shí)候統(tǒng)一轉(zhuǎn)成OpenGL的指令執(zhí)行,能最大限度的提高圖形的處理效率。所以在上面提到的繪制上下文中,會(huì)提供一個(gè)可以記錄繪制命令的畫(huà)布(Canvas)。每一個(gè)獨(dú)立的繪制上下文可以看作是一個(gè)圖層。

為了提高性能,這里引入了圖層(Layer)的概念。通常繪制會(huì)將渲染內(nèi)容分為多個(gè)層進(jìn)行加速。對(duì)于會(huì)頻繁變化的內(nèi)容,將其單獨(dú)創(chuàng)建一個(gè)圖層,那么這個(gè)獨(dú)立圖層的頻繁刷新就不必導(dǎo)致其他內(nèi)容重新繪制,從而達(dá)到提升性能并減少功耗的效果,同時(shí)還可以支持GPU緩存等優(yōu)化。每個(gè)RenderNode都可以決定自己是否需要單獨(dú)分層。

如下圖所示,繪制流程會(huì)從需要繪制的節(jié)點(diǎn)中,挑選最近的且需要分層的節(jié)點(diǎn)開(kāi)始,自頂向下的執(zhí)行每個(gè)節(jié)點(diǎn)的Paint方法。

\

對(duì)每個(gè)節(jié)點(diǎn),繪制分為四個(gè)步驟:

  1. 如果當(dāng)前節(jié)點(diǎn)需要分層,那么需要?jiǎng)?chuàng)建一個(gè)新的繪制上下文,并提供可以記錄繪制命令的畫(huà)布
  2. 在當(dāng)前的畫(huà)布上記錄背景的繪制命令
  3. 遞歸調(diào)用子節(jié)點(diǎn)的繪制方法,記錄子節(jié)點(diǎn)的繪制命令
  4. 在當(dāng)前的畫(huà)布上記錄前景的繪制命令

一次完整的繪制流程結(jié)束后,我們會(huì)得到一棵完整的Layer樹(shù),Layer樹(shù)上包含了這一幀完整的繪制信息:包括每一層的位置、transform信息、Clip信息、以及每個(gè)元素的繪制命令。下一步就要經(jīng)過(guò)光柵化和合成的過(guò)程,將這一幀的內(nèi)容顯示到界面。

5)光柵化合成機(jī)制

在上面的繪制流程結(jié)束后,會(huì)通知GPU線(xiàn)程開(kāi)始進(jìn)行合成的流程。

\

如上圖所示,UI線(xiàn)程(UI Thread)在渲染管線(xiàn)中的輸出是LayerTree,它相當(dāng)于一個(gè)生產(chǎn)者,將生產(chǎn)的LayerTree添加到渲染隊(duì)列中。GPU線(xiàn)程(GPU Thread)的合成器(Compositor)相當(dāng)于消費(fèi)者,每個(gè)新的渲染周期中,合成器會(huì)從渲染隊(duì)列中獲取一個(gè)LayerTree進(jìn)行合成消費(fèi)。

對(duì)于需要緩存的Layer,還要執(zhí)行光柵化生成GPU紋理,所謂光柵化就是將Layer里面記錄的命令進(jìn)行回放,生成每個(gè)實(shí)體的像素的過(guò)程。像素是存儲(chǔ)在紋理的圖形內(nèi)存中。

合成器會(huì)從系統(tǒng)的窗口中獲取當(dāng)前的Surface,將每個(gè)Layer生成的紋理進(jìn)行合成,最終合成到當(dāng)前Surface的圖形內(nèi)存(Graphic Buffer)中。這塊內(nèi)存中存儲(chǔ)的就是當(dāng)前幀的渲染結(jié)果內(nèi)容。最終還需要將渲染結(jié)果提交到系統(tǒng)合成器中合成顯示。系統(tǒng)的合成過(guò)程如下圖所示:

\

當(dāng)GPU線(xiàn)程的合成器完成一幀的合成后,會(huì)進(jìn)行一次SwapBuffer的操作,將生成的Graphic Buffer提交到與系統(tǒng)合成器建立的幀緩沖隊(duì)列(Buffer Queue)中。系統(tǒng)合成器會(huì)從各個(gè)生產(chǎn)端獲取最新的內(nèi)容進(jìn)行最終的合成,以上圖為例,系統(tǒng)合成器會(huì)將當(dāng)前應(yīng)用的內(nèi)容和系統(tǒng)其它的顯示內(nèi)容,例如System UI的狀態(tài)欄、導(dǎo)航欄,進(jìn)行一次合成,最終寫(xiě)入到屏幕對(duì)應(yīng)的幀緩沖區(qū)(Frame Buffer)中。液晶屏的驅(qū)動(dòng)就會(huì)從緩沖區(qū)讀取內(nèi)容進(jìn)行屏幕的刷新,最終將內(nèi)容顯示到屏幕上。

6)局部更新機(jī)制

經(jīng)過(guò)上面1~5的流程,完成了首次完整的渲染的流程,在后續(xù)的運(yùn)行中,例如用戶(hù)輸入、動(dòng)畫(huà)、數(shù)據(jù)改變都有可能造成頁(yè)面的刷新,如果只是部分元素發(fā)生了變化,并不需要全局的刷新,只需要啟動(dòng)局部更新即可。那么局部更新是怎么做到的?下面我們介紹一下局部 更新的流程。

\

以上圖為例,JS在代碼中更新了數(shù)據(jù),通過(guò)數(shù)據(jù)綁定模塊會(huì)自動(dòng)觸發(fā)前端組件屬性的更新,然后通過(guò)JS引擎異步發(fā)起更新屬性的請(qǐng)求。前端組件會(huì)根據(jù)變更的屬性,構(gòu)建一組新的Composed的補(bǔ)丁(Patch),作為渲染管線(xiàn)更新的輸入。

\

如上圖所示,在下一個(gè)VSync到來(lái)時(shí),渲染管線(xiàn)會(huì)在UI線(xiàn)程開(kāi)始更新的流程。通過(guò)Composed補(bǔ)丁的Id,在ComposedMap中查詢(xún)到對(duì)應(yīng)的ComposedElement在Element樹(shù)上的位置。通過(guò)補(bǔ)丁對(duì)Element樹(shù)進(jìn)行更新。以ComposedElement為起始,逐層進(jìn)行對(duì)比,如果節(jié)點(diǎn)類(lèi)型一致則直接更新對(duì)應(yīng)屬性和對(duì)應(yīng)的RenderNode,如果不一致則重新創(chuàng)建新的Element和RenderNode。并將相關(guān)的RenderNode標(biāo)記為needLayout和needRender。

\

如上圖所示,根據(jù)標(biāo)記需要重新布局和重新渲染的RenderNode,從最近的布局邊界和繪制圖層進(jìn)行布局和繪制的流程,生成新的Layer樹(shù),只需要重新生成變更RenderNode對(duì)應(yīng)的Layer即可。

\

如上圖所示,接下來(lái),根據(jù)刷新后的Layer樹(shù)作為輸入,在GPU線(xiàn)程進(jìn)行光柵化和合成。對(duì)于已經(jīng)緩存的Layer則不需要重新光柵化,合成器只需要將已緩存的Layer和未緩存或更新的Layer重新合成即可。最終經(jīng)過(guò)系統(tǒng)合成器的合成,就會(huì)將新一幀的內(nèi)容顯示。

以上就是一個(gè)ACE JS應(yīng)用的渲染及更新的流程。最后,通過(guò)兩張流程圖回顧一下整體的流程:

\

了解完ACE JS應(yīng)用的渲染及更新的流程,如果大家想了解更多關(guān)于HarmonyOS UI框架如何解決設(shè)備形態(tài)差異帶來(lái)的開(kāi)發(fā)挑戰(zhàn)和應(yīng)用示例,可參考我們之前推出的內(nèi)容:解密HarmonyOS UI框架:

https://mp.weixin.qq.com/s/0RZL09vKppIZmpqTJUeRSA。

ACE UI框架目前的成熟度以及演進(jìn)

截至目前,ACE UI框架已商用落地了華為運(yùn)動(dòng)手表,華為智能手表,華為智慧屏,華為手機(jī),華為平板等一系列產(chǎn)品。使用場(chǎng)景包括日歷、出行、健身、實(shí)用工具等各類(lèi)應(yīng)用,手機(jī)-設(shè)備碰一碰全品類(lèi)的應(yīng)用,以及今年六月份發(fā)布的HarmonyOS中各類(lèi)的服務(wù)卡片-圖庫(kù)、相機(jī)等。另外,在開(kāi)發(fā)調(diào)測(cè)方面,開(kāi)發(fā)者工具(HUAWEI DevEco Studio)中也集成了ACE UI框架,支持在PC端(MacOS,Windows)上的開(kāi)發(fā)調(diào)測(cè),實(shí)時(shí)預(yù)覽(包括實(shí)時(shí)多設(shè)備預(yù)覽,組件級(jí)預(yù)覽,雙向預(yù)覽等),實(shí)現(xiàn)了在PC上和設(shè)備上一致的渲染體驗(yàn)。

未來(lái),面向開(kāi)發(fā)者的極簡(jiǎn)開(kāi)發(fā),面向消費(fèi)者的流暢酷炫的體驗(yàn),以及能夠高效在不同設(shè)備不同平臺(tái)上部署,ACE UI框架會(huì)繼續(xù)沿著精簡(jiǎn)開(kāi)發(fā)和高性能兩個(gè)方面演進(jìn),結(jié)合語(yǔ)言更進(jìn)一步簡(jiǎn)化開(kāi)發(fā)范式,結(jié)合運(yùn)行時(shí)在跨語(yǔ)言交互,類(lèi)型優(yōu)化等方面進(jìn)一步增強(qiáng)性能體驗(yàn),結(jié)合分布式能力將編程模型從MVVM演進(jìn)到分布式MVVM(Distributed Model-View-ViewModel)等。采用類(lèi)自然語(yǔ)言的聲明式UI描述進(jìn)行界面搭建,編程語(yǔ)言也進(jìn)一步開(kāi)放,未來(lái)考慮向TS進(jìn)行演進(jìn),從動(dòng)效、布局和性能方面進(jìn)一步提升用戶(hù)使用體驗(yàn)。

當(dāng)然,應(yīng)用生態(tài)還會(huì)涉及更多的方面,比如三方插件的繁榮,跨OS平臺(tái)的擴(kuò)展,更具創(chuàng)新的分布式體驗(yàn)等等。ACE UI框架還很年輕,期待和眾多開(kāi)發(fā)者一起,重點(diǎn)圍繞著多設(shè)備組成的超級(jí)終端的新興場(chǎng)景,不斷打磨完善,共同構(gòu)建領(lǐng)先的應(yīng)用體驗(yàn)和生態(tài)!

目前HarmonyOS的線(xiàn)上開(kāi)發(fā)體驗(yàn)已上架,歡迎大家在線(xiàn)體驗(yàn)。ACE JS框架已經(jīng)進(jìn)駐開(kāi)源社區(qū),歡迎關(guān)注與共建,期待諸位一起共建我們的開(kāi)發(fā)框架,有開(kāi)發(fā)過(guò)程中的疑問(wèn)和對(duì)HarmonyOS開(kāi)發(fā)的好的建議歡迎登錄論壇,我們一起探討。


關(guān)鍵詞:HarmonyOS