Cube 技术解读 | 详解「支付宝」全新的卡片技术栈
京君圍繞 Cube 技術的架構邏輯,闡述其渲染和生產過程,并指導開發者完成初階的技術調試。
?點擊播放,可觀看 CodeHub#7 完整回放;公眾號mPaaS后臺回復“Cube”可獲取講師完整PPT。
動態卡片的背景
從 Windows 時代開始,應用程序圖標就成了用戶(流量)的主入口,并且一直持續到移動端時代。圖標即入口的方式,缺點是不直觀,最少需要一次點擊后才能接觸到想要的信息。在此背景下,iOS系統和部分Android系統實現了把內容和服務前置的卡片,如下圖所示:
此外,鴻蒙系統也提出了類似的卡片場景,作為某種流量入口。實際上,在應用內部的卡片作為內容展示以及服務入口的場景則更為普遍,比如支付寶首頁和招行銀行的理財頁面,其中每個小矩形都是一個卡片。對于運營來說,卡片樣式和內容可以隨時配置,不用等待應用版本升級,也是某種剛需。
魔方卡片(Cube)概要
魔方卡片(Cube)是螞蟻集團內部自研的一套跨平臺動態化卡片解決方案,是服務于應用頁面內的區域動態化技術,面向內容運營,幫助產品技術提高開發效率和運營效率。每一個魔方卡片(Cube)獨立嵌在原生頁面內的一個區域,區域內容通過卡片模版進行展示。
這里展開講下高性能。魔方卡片(Cube)追求的是接近native原生體驗。我們定義了兩個維度:
一個是極致的性能。在Cube小程序能力的基礎上,我們去掉了一些復雜的css能力,例如偽類偽元素、inline/block等,同時也對js的能力做了限制(魔方卡片使用quickjs作為腳本引擎)。此外,我們還對quickjs做了一些優化,包括不限于離線atom編譯優化,異步gc優化等。我們也引入了wamr作為quickjs的“協處理器”,支持用戶使用javascript和assemblyscript混合開發。這樣用戶可以用assemblyscript一些熱點函數或者模塊。
另一個維度是極致的內存。在信息瀑布場景無限下拉,魔方卡片的內存增長接近Native卡片。我們對卡片的能力做了比較精細分級,通過在開發時配置,減小運行時的內存消耗。下圖展示了一個簡單卡片,如圖所示魔方卡片的工程目錄,以及錢包某個卡片的真實代碼和運行效果。
Cube卡片的生產&工作流程
- 研發期
- 本地開發
魔方卡片(Cube)配套獨立的開發工具,支持卡片的編譯、日志輸出、實時預覽等功能,vue作為當前開發模版的dsl語言,支持js、css編輯卡片樣式。
- 卡片管理
卡片本地開發完成后,通過卡片管理后臺將卡片編譯產物上傳發布,可以對卡片進行版本管理,卡片發布后就可以在客戶端進行卡片的動態更新。
- 運行期
為了方便端上業務接入魔方卡片,我們引入了一個魔方卡片容器(CardSDK)的概念。CardSDK把一些和業務層/服務端聯系緊密的,且通用能力做了一些封裝。例如我們通過CubeCardSdk從服務端拉取卡片和業務數據。此外CardSDK也負責常用的JSAPI、第三方組件的接入。這樣魔方卡片能夠更專注于卡片產品本身。
核心系統架構
魔方卡片(Cube)的系統架構主要包括JSEngine、CardEngine、RenderEngine和Platform幾部分,絕大部分代碼都是C++實現。
- JSEngine
主要負責卡片js邏輯執行和卡片數據變化監聽,從而支持開發者在卡片內部寫一些業務邏輯能力實現卡片內容和樣式的動態變化。
因為卡片場景對性能要求較高,綜合包大小和性能等方面考慮,我們選擇了quickjs作為我們的js基礎引擎庫,同時實現了一個非常小的js響應式框架(JSFM),用來支持卡片內的邏輯代碼能力。
- CardEngine
主要負責卡片數據的解析和綁定、卡片邏輯渲染、構建DOM指令、JSAPI管理、JSBinding、Native事件通信等。
卡片DOM樹的初始化構建過程,我們并沒有把它放在js運行時,而是在卡片實例初始化鏈路中直接通過C++進行指令生成和樹構建,一方面是為了保持js框架更小更快,另一方面C++的運行效率更高。
- RenderEngine
后端渲染底座,負責卡片布局計算、樣式解析、Layer計算、自繪制組件、同層渲染、光柵化上屏等過程,以及手勢、動效等交互效果。
- Platform
平臺相關接口,包括原子view封裝、Canvas API、三方組件擴展協議、動畫api等。
線程模型和數據模型
線程模型
魔方卡片(Cube)生命周期內的主要線程包括業務線程和引擎線程,業務線程是卡片數據的初始化階段由業務發起執行,是卡片生命周期的beforeCreate階段。引擎線程是所有卡片生命周期運行階段的共有線程,主要包括Bridge線程、Render線程、Paint線程和UI主線程。
- Bridge線程
js運行時線程,也是Dom節點數據查詢和處理線程,因為基于魔方卡片小、快的定位,js邏輯只是卡片一個輔助能力,不具備過于復雜業務邏輯能力,所以Bridge線程相對較輕,并設計為單線程模式。
- Render線程
渲染相關數據計算線程,包括渲染樹構建、節點層級計算、Layer分層繪制計算、手勢數據計算以及渲染任務構建,Render過程主要涉及樹的遞歸計算過程,相對渲染過程耗時很短, 設計為單線程模式。
- Paint線程
繪制線程,執行卡片節點分層繪制及光柵化任務。Paint線程并不是一個固定的線程,根據當前任務模型,Paint線程可能是主線程,也可能是一個線程池里的子線程;在同步渲染模式下,Paint線程直接是主線程;而在異步渲染模式下,通過一個線程池來實現Paint任務的并發渲染,提高渲染效率,例如在列表滑動場景。
- UI主線程
UI操作主線程,即為目前的平臺線程,主要包括手勢識別、UI上屏和三方擴展組件的數據更新等。
除了以上涉及的主要線程外,還有埋點和監控相關的playground后臺線程,整體優先級比較低。整體的線程模型設計,最大限度減少UI主線程壓力,提高卡片并發渲染效率。但目前還有一些不足,包括UI線程切換頻繁、Bridge線程越來越重等,后面會繼續優化線程模型。
數據模型
和線程模型對應的數據模型主要包括三棵樹:NodeTree、RenderTree、LayerTree,除此之外,還存在一個臨時的PaintTree;
- NodeTree
卡片原始節點樹,對應前端的Dom樹,引擎會根據NodeTree做樣式解析和布局計算;
- RenderTree
渲染數據樹,這是一顆變形樹,很多情況下它的樹層級結構和NodeTree是一樣的,其實當初在設計定義引擎數據模型的時候,我們討論過到底要不要這棵樹,有沒有必要存在這樣一顆和NodeTree層級一樣的樹,最終我們還是保留了,原因是這棵樹可以比較靈活的調整樹關系,如果把卡片分為布局階段和渲染階段,那么這顆樹就是渲染階段的源樹。
事實證明我們的決定是正確的, 我們后續支持的zindex/static等能力,都是因為這棵樹的存在可以在引擎層很好的去支持, 而不用在平臺層去模擬實現這種層級變更能力從而導致很有限的場景支持,包括以后我們做渲染快照技術也可以從這顆樹去考慮。
- LayerTree
LayerTree樹,顧名思義就是一個分層樹,在RenderTree基礎上對節點進行分層,同一層的節點在同一個渲染任務管線內做繪制光柵化,不同層之間相互獨立,可以并發渲染。
- PaintTree
PaintTree是一個臨時樹,其實嚴格的說是一個拷貝樹,是通過RenderTree拷貝一個子樹,每次發生渲染時臨時生成,當然也會做些節點優化處理,例如被完全蓋住的節點會被優化調,避免重復渲染。每一個layer上存在一個PaintTree,通過PaintTree進行節點繪制生成光柵化指令或位圖。
高性能列表渲染
對于列表內使用卡片的場景,主要考慮的是卡頓影響,尤其是中低端機設備。魔方卡片(Cube)支持異步渲染,所以在列表場景下可以很流暢,同時因為支持多線程并發能力,可以多張卡片并發渲染,所以在異步渲染條件下也不會有明顯的白屏效果。
Native技術優化
我們期望卡片服務于頁面內區域化內容動態展現和簡單業務邏輯,更多的是面向移動端開發者。即使我們使用的卡片DSL語言描述是前端語言,我們也希望能夠對CSS的使用做約束、支持有限的CSS能力,但同時也希望盡可能覆蓋到一些開發者常用的CSS能力。
所以我們針對CSS能力做了一個專項工作,和前端團隊技術同學一起做了魔方卡片(Cube)CSS能力規范,對CSS能力做了約束限制。即便如此,在Native渲染引擎下,想非常好的去支持這些能力,也是有很多困難,包括zindex的支持、overflow等,因此我們也基于一些依賴的平臺能力也做了優化處理。
Layer容器
我們引入Layer容器概念,前面介紹數據模型時,提到了LayerTree,每一個Layer節點是一個獨立的渲染容器,由平臺View作為Layer容器來渲染其他虛擬節點。如果按照常規的做法是一個View對應一個渲染容器,我們使用兩個View組合為Layer容器(iOS使用CALayer),將內容層和邏輯層分離,這樣做的好處很多,例如layer節點的shadow繪制限制裁剪問題、內容層的畫布切割優化等。
手勢改造
手勢的優化改造主要為了解決平臺系統手勢分發能力的限制,不管是Android平臺還是iOS平臺,系統對手勢的分發處理都有一些限制,例如兄弟節點不能分發事件(iOS)、超過父節點區域無法接收事件(影響overflow能力)等,所以需要對手勢進行改造。
因為卡片渲染支持三方組件擴展,為了不影響擴展組件的事件響應,我們基于Layer容器接管改造系統手勢行為,內部進行容器節點的手勢分發管理,而對于存在三方組件混合渲染的場景,Layer容器和三方組件之間的手勢分發保持系統行為。
光柵化
魔方卡片(Cube)渲染過程包括指令渲染和位圖渲染兩種渲染模式,這兩種模式會在不同場景條件下切換,用來優化不同場景下性能,例如幀率和內存。位圖渲染在Android上相對比較復雜。默認是用Bitmap作為離線渲染的緩存,缺點是引入一次額外cpu/gpu內存拷貝并且無法充分利用GPU資源,優勢是兼容性好。我們嘗試過使用textureview作為離線渲染緩沖,發現6.0以下設備存在嚴重的兼容性問題,而且不同設備之間的穩定性差別巨大。
同時光柵化能力依賴平臺系統的Canvas API,有些高階方法會涉及硬件加速的限制,包括shadow api以及系統對glRender buffer的限制(Android平臺),我們也對大畫布場景做了視圖切割分段渲染來保證渲染性能。我們同時也在著手用Skia Canvas api替代平臺層的Canvas API。
同層渲染
魔方卡片(Cube)把三方組件當作獨立一層layer單獨進行數據更新,可以非常方便高效的接入擴展的三方組件。基于系統的UI能力,使擴展組件在卡片內天生統一渲染。同時支持組件在不同卡片上的復用。在實際的業務場景中,同層渲染也帶來了很多額外的問題。諸如地圖/視頻/動畫等組件,一般會伴隨著較大的性能內存開銷。這些開銷對卡片的渲染會有負面影響,尤其在列表滾動時。對于地圖/視頻組件,我們配合組件提供方case by case的解決問題,并且試圖在卡片上線時設置卡點。對于動畫組件,Cube持續的在擴展屬性動畫/幀動畫能力,并且內置canvas能力。
魔方卡片(Cube)的現狀和規劃
目前魔方卡片(Cube)已經服務「支付寶」的首頁、證券(股票)、卡包、出行等20+的業務場景,日pv超過100億。在未來相當長的一段時間內,「支付寶」內部的業務場景,將逐步把存量的native卡片和h5卡片Cube化。
因此,我們一方面會持續把開發者體驗做好,諸如開發調試工具鏈條;另一方面我們也將持續的優化基礎性能,諸如追求更小的包體積,更低的內存等。
同時,魔方卡片(Cube)Beta 已上線 mPaaS 供廣大移動開發者使用,公測期間,登錄 mPaaS 控制臺,立即贈送十個卡片模版免費使用。> > 點擊這里 < <體驗魔方卡片。
卡片未來規劃的另一個方向是物聯網設備(例如RTOS)的應用開發棧。準確說不是魔方卡片,而是卡片和小程序的某種中間形態。
物聯網設備的界面一般比較簡單,近似卡片;但是又需要多個“卡片”之間的路由能力,更接近于應用的形態。這樣一個混合形態既能保留魔方卡片(Cube)在內存/性能/包體積上的優勢,又能滿足物聯網設備應用開發的訴求。
根據我們的調研,大部分RTOS應用開發環境還是停留在傳統的C語言,效能和動態性都不理想。因此對于開發者來說,Cube 也許是一個不錯的選擇。
推薦閱讀:《Cube 技術解讀 | 支付寶新一代動態化技術架構與選型綜述》
> > 點擊這里 < <體驗魔方卡片。
原文鏈接:https://developer.aliyun.com/article/801630?
版權聲明:本文內容由阿里云實名注冊用戶自發貢獻,版權歸原作者所有,阿里云開發者社區不擁有其著作權,亦不承擔相應法律責任。具體規則請查看《阿里云開發者社區用戶服務協議》和《阿里云開發者社區知識產權保護指引》。如果您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將立刻刪除涉嫌侵權內容。總結
以上是生活随笔為你收集整理的Cube 技术解读 | 详解「支付宝」全新的卡片技术栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 低代码这么火,它的人才认证你考了吗?
- 下一篇: Serverless 工程实践 | 快速