1. 程式人生 > >美團 iOS 端開源框架 Graver 在動態化上的探索與實踐

美團 iOS 端開源框架 Graver 在動態化上的探索與實踐

近些年,移動端動態化技術可謂是“百花齊放”,其中的渲染效能也是動態化技術一直在探索、研究的課題。美團的開源框架 Graver 也為解決動態化框架的渲染效能問題提供了一種新思路:關於佈局,我們可以採用“畫控制元件”方案替代傳統的“拼控制元件”方式。本文嘗試給出一些探索思考與實踐經驗的分享。

前言

動態化技術指的是不依賴程式安裝包,就能進行動態實時更新頁面的技術。特別是對於電商、社交等需要快速迭代、實時調整的強運營類業務來說,動態化具有非常重要意義。它的優勢主要表現為:提高人效、縮短迭代試錯週期、解決版本長尾問題、減少包大小等等。

從2018年開始,移動端裝置的增長紅利不再,整個生態增長趨勢開始由高走低,與之對應的開發生態在 Native 技術方向也逐漸開始進入低迷階段,大方向在向跨平臺演進,方案上已經是“百花齊放”。現有的客戶端動態化技術主要可以劃分為以基於 Webview 的 Web 頁面動態化載入、本地內建多個模板支援動態切換、支援動態 DSL 的佈局引擎以及基於虛擬機器等四類。

圖1 動態化技術分類 

動態化方案的渲染引擎多數是基於原生 UI 控制元件搭建動態化頁面。基於 Webview 的 Web 頁面動態化,實質是基於瀏覽器執行網頁,頁面繪製效率、執行效率相對較低一些。而後三種解決方案,分別通過建立對映表、佈局引擎、虛擬機器與客戶端渲染引擎通訊及呼叫關係,渲染引擎則都是基於原生 UI 控制元件搭建動態化頁面。由於作業系統提供的 UI 控制元件佈局/繪製僅支援主執行緒訪問,大量原生 UI 控制元件操作導致 CPU/GPU 負擔過重,所以在構建複雜的動態化頁面上存在效率和效能瓶頸。因此,渲染效能是動態化技術一直在探索、研究的課題。本文嘗試給出一些探索思考與實踐經驗的分享。

圖2 UI渲染流程

動態佈局框架

如前言部分的圖1所示,MTFlexbox 是美團點評自研的一款跨平臺動態佈局框架,它遵循了 CSS3 中提出的 Flexbox 規範來抹平多端差異。美團 App 首頁、搜尋結果頁等業務有一個共同點,就是面向的業務方比較多,承載了流量輸送變現的能力。在檢視層面呈現輕互動、重展示的特徵。頻繁變動 UI,快速上線是一個剛需,MTFlexbox 正是滿足了這樣一個剛需。

由於本文側重對 MTFlexbox 的渲染效能優化,故僅對 MTFlexbox 做概括介紹。MTFlexbox 首先定義了一份跨平臺統一的 DSL 佈局描述檔案,前端通過編輯器編輯產生布局檔案並上傳到雲端,客戶端下載佈局檔案然後根據佈局中的描述資訊繫結業務資料,最後基於原生 UI 控制元件搭建檢視並渲染展示。MTFlexbox 的工作原理如下圖所示:

圖3 MTFlexbox 工作原理

業務痛點

然而,隨著業務的迭代演變,美團 App 首頁、搜尋結果頁等業務檢視卡片樣式越來越多,展示也越來越複雜。樣式種類多意味著檢視複用率低,極端場景下甚至無法進行復用。展示覆雜,同時也意味著控制元件數量多、佈局複雜、層級深。如果大量複雜操作都發生在主執行緒,難免造成渲染卡頓等使用者體驗方面的問題。

圖4 業務痛點

針對上述問題,外賣終端使用者研發組、美團終端技術研發組、美團終端業務研發組合作共贏,三方協調資源成立了跨部門、跨事業部的虛擬專項聯合專案組,三方精誠合作,在技術上不斷追求卓越,力求同時保證穩定性、動態化和高效能。

圖5 合作共贏

思路分析

動態佈局框架 MTFlexbox 通過系統 UIKit 搭建檢視並渲染展示,其測量、佈局、繪製過程均發生在主執行緒。而作為一款 iOS 端高效的 UI 非同步渲染框架 Graver,其佈局計算、渲染過程完全非同步化,整個過程結束後才通知 UI 執行緒進行展示。這給我們解決動態化框架的渲染效能問題打開了新思路:關於佈局,我們可以採用“畫控制元件”方案替代傳統的“拼控制元件”方式。Graver 已經在美團 App 的外賣頻道、獨立外賣 App 核心業務場景的多個業務中經歷了一年多的實踐檢驗。良好的穩定性和出色的渲染效能,也得到了美團外賣內部技術團隊的認可和肯定。關於 Graver 更多的內容這裡不再贅述,詳細介紹請參考另一篇技術部落格:《美團開源 Graver 框架:用“雕刻”詮釋 iOS 端 UI 介面的高效渲染》 。

如何構建基於 Graver 進行非同步渲染的動態化框架(MTFlexbox),成為首先需要解決的問題。

圖6-1 拼控制元件

圖6-2 畫控制元件

“拼控制元件”到“畫控制元件”

通過對系統 UI 渲染流程分析不難發現:唯一確定一個檢視展示僅需要確定檢視佈局資訊、內容資訊、渲染資訊三個要素。含義如下:

  • 佈局資訊:UI 控制元件的大小、位置和展示層級。
  • 內容資訊:UI 展示的文字、圖片等。
  • 渲染資訊:包括UI控制元件的背景色、展示字型字號、透明度、邊框等控制元件視覺屬性。

Graver 的每個繪製元素通過 WMMutableAttributedItem 來表達內容資訊、渲染資訊,CGRect 表達繪製元素的大小和位置。渲染整個過程除畫板檢視外,完全沒有使用 UIKit 控制元件,最終產出的結果是一張點陣圖(Bitmap)。如果能通過一棵樹形結構組織所有的繪製元素即繪製結點樹,即可按照遞迴遍歷的方式“畫控制元件”來轉義“拼控制元件”構建檢視。接下來,我們需要思考如何建立 MTFlexbox 的資料結構與繪製結點樹之間的關係,並且保證該轉化過程完全非同步化。

構建虛擬結點樹

如開篇動態佈局框架章節 MTFlexbox 的原理所描述:在相繼完成模板樹構建、資料繫結之後即進行了檢視樹構建。然而,出於功能劃分考慮、兼顧保留 MTFlexbox 的系統 UI 渲染引擎能力以及構建繪製結點樹需要的必要資訊考慮,需要構建一箇中間資料結構:虛擬結點樹。它應包含樹形結構的層級資訊、Flex 屬性資訊、資料解析處理後的內容資訊以及基本的渲染資訊。虛擬結點樹是既能構建 UI 控制元件樹也能構建繪製結點樹的“橋樑”。

圖7 MTFlexbox 改造

設計資料流

通過上述思路分析,確定了關鍵資料結構:虛擬結點樹、繪製結點樹。接下來,我們需要思考如何構建虛擬結點樹到繪製結點樹的資料流。

在前端有兩個重要的概念:迴流、重繪。

  • 迴流:當我們對結點樹的修改引發了幾何尺寸的變化(比如修改元素的寬、高或隱藏元素等)時,需要重新計算元素的幾何屬性(其他元素的幾何屬性和位置也會因此受到影響),然後再將計算的結果繪製出來,這個過程就是迴流(也叫重排)。
  • 重繪:當我們對結點樹的修改導致了樣式的變化,卻並未影響其幾何屬性(比如修改了顏色或背景色)時,不需重新計算元素的幾何屬性,可以直接為該元素繪製新的樣式。

參考前端技術思想以及考慮單一職責原則,在虛擬結點樹與繪製結點樹中間構建 Fat 型渲染結點樹和 Thin 型渲染結點樹。Fat 型渲染結點樹負責儲存原始資料以便做邏輯處理,Thin 型渲染結點樹負責儲存位置、大小和內容資訊。當有修改不影響幾何尺寸變化的情況下,僅重新生成 Thin 型渲染結點樹的內容資訊即可。

圖8 虛擬結點樹到點陣圖的轉換

執行緒安全考慮

提高渲染效能的關鍵,即是全力保證主執行緒的最小資源開銷。因此,需要思考如何保證虛擬結點樹到繪製結點樹的轉換過程是執行緒安全的。Facebook 開源的跨平臺佈局引擎 Yoga,提供了通過 UI 檢視樹中 Flex 屬性計算得出每個 UI 控制元件的位置和大小。然而,提供給 iOS 平臺的插頭類是基於 UIView 的,即佈局計算過程必須在主執行緒。需要基於 Yoga 核心邏輯重新封裝基於渲染結點樹的計算邏輯,以保證佈局計算是執行緒安全的。如下圖所示:

圖9 基於 Yoga 的佈局計算執行緒安全式改造

架構設計

有了上述的思路分析,接下來我們開始著手 Graver 接入 MTFlexbox 的架構設計。Graver 需要作為獨立渲染引擎存在,並保留接入多種動態化框架的可能,這是出於架構設計的靈活性和擴充套件性的考慮。接入層命名為 M-Graver,其上層基於 MTFlexbox 進行擴充套件但可靈活插拔,下層基於 Graver 渲染引擎,如下圖10所示:

圖10 M-Graver 架構

M-Graver 是執行緒安全的,其主要分為解析層、聚合層、佈局層和預備層。下面對各層分別做簡單的介紹:

  • 解析層:關於輸入定義了一個不依賴於上層 DSL 描述語言的標準化虛擬結點樹結構;解析層主要進行虛擬結點樹到渲染結點樹的轉換,涉及標籤解析、渲染資訊解析、統計資訊解析以及 Flex 屬性解析。
  • 聚合層:完成渲染結點樹的最後構造,涉及可見性、互動性等處理;關於事件繫結也是在聚合層完成,每個事件都以行為引數化的形式繫結到相應渲染結點上。
  • 佈局層:利用 Yoga 完成每個結點的 Frame 計算以及層級資訊處理,同時將渲染資訊等內容轉換到 Graver 框架可識別的資料。
  • 預備層:進行座標系轉換,並拍平帶有檢視層級結構資訊的渲染結點樹,剔除無效渲染結點(如可見性、size 為0等),遮蔽掉由於檢視層級原因導致被完全遮擋的渲染結點,最後根據渲染結點生成繪製結點構建繪製結點樹,交由 Graver 提供的畫板檢視進行繪製。

技術難點問題

按照上述思路分析完成架構設計,但在實施部署的過程中也遇到了不少的技術難點和問題。如:動態佈局框架 MTFlexbox 建立至今已兩年有餘,因業務的快速發展而產生了一些技術“負債”。為了保證不影響線上原有的業務邏輯,所以在進行 MTFlexbox 的模板樹到虛擬結點樹,再到 UI 檢視樹的技術升級改造過程時,尤其需要關注各種“蛛絲馬跡”式的細微邏輯。

另外,在將非同步渲染引擎 Graver 接入 MTFlexbox 的過程中也遇到了諸多問題,包括如何構建基於點陣圖的事件處理系統,跨渲染引擎的技術融合,一些極端場景下的繪製效率瓶頸等等。下面將逐一展開闡述。

1. 如何基於點陣圖進行事件處理

由於檢視最終通過渲染點陣圖來呈現,這就需要建立基於點陣圖的事件處理系統。如前文所述,渲染結點樹記錄了每個控制元件的位置、大小資訊以及層級結構,基於此可仿照系統事件處理邏輯進行基於點陣圖的事件處理系統設計。在檢視展示期間,畫板檢視收到事件響應通知後(如點選了畫板檢視中標號為5的紅色按鈕),根據點陣圖對應的渲染結點樹儲存的各控制元件佈局、層級和渲染資訊,逐層遍歷找到需要響應的渲染結點,如果涉及資訊修改則變更其在渲染結點樹中的渲染資訊,觸發再次渲染的同時執行該渲染結點繫結的事件方法。遍歷渲染結點樹的輸入是系統基於畫板檢視返回的點選位置,其遍歷過程與系統 UI 事件查詢過程比較相似。事件處理過程如下圖11所示:

圖11

2. 系統 UI 控制元件與繪製元素的融合問題

從美團業務特徵出發,圖文組合佔據多數 UI 場景。然而也存在諸如動效等無法依託 Graver 進行圖文渲染的情況。因此,需要考慮跨渲染引擎的渲染融合問題。在 M-Graver 的預備層遍歷渲染結點樹時,可以根據當前結點是否為原生結點決議樹拆分,如果是原生結點,將該結點連同其子樹“直系”繪製結點從渲染結點樹中拆分下來,以該結點為根結點的子渲染結點樹,生成對應的繪製結點樹,多個子渲染結點樹的根結點,構成了以畫板檢視為單元的畫板檢視樹。如下圖12所示:

圖12 結點融合

為了便於理解,我們給出以下幾個名詞的解釋說明:

  • 繪製結點:渲染結點樹中的結點如果可以轉化成繪製結點樹中的結點,則稱之為繪製結點,最後通過 Graver 進行渲染。
  • 原生結點:渲染結點樹中的結點如果不能轉化成繪製結點樹中的結點,只能轉化成系統 UI 控制元件則稱之為原生結點。
  • 結點變異:以一個二叉渲染結點樹為例,左子結點是原生結點,右子結點是繪製結點的情況下,由於左子結點先於右子結點新增到父節點,可能存在層級顛倒問題。這時右子結點需要強制轉為原生結點,維持正確的層級順序,即結點變異。
  • 畫板檢視:繼承自 UIView 的普通檢視,其內部封裝了一系列的基於 Graver 的渲染邏輯。

樹拆分的過程還涉及到兄弟結點層級顛倒以及佈局交叉等問題。兄弟結點層級顛倒問題通過結點變異來解決。佈局交叉問題存在於判定渲染結點樹的結點是繪製結點或原生結點之前,由於佈局原因存在檢視交叉。佈局交叉問題通過新建畫板檢視插入來保證層級正確以及繪製正確。由於篇幅有限,這裡不再贅述。

3. 極端場景下的繪製效率瓶頸問題

從產品互動層面看,為了提高屏效往往存在多向滑動的檢視元件場景。如橫滑 Scroll 元件,其特點是需要通過滑動才能逐漸看到所有的檢視內容。通過非同步渲染繪製點陣圖來實現的情況下,存在單一併發渲染任務計算邏輯繁重的問題,從使用者體驗層面看容易造成“白屏”現象。為解決該問題,將檢視卡片渲染過程分解,進行增量渲染,採用漸進式的方式減少空白頁面等待時間。根據待展示區域在螢幕中相對位置進行區塊劃分,通過佇列集中控制繪製操作。以此進一步提高併發效率,並減少渲染過程的非必要系統資源消耗。

區塊劃分

區塊劃分策略的實質是繪製結點樹的拆分,將繪製結點樹中不存在佈局交叉的子結點樹進行逐一拆分,每個拆分下來的繪製結點子樹即為一個區塊,同時要設定最小塊策略,否則拆分粒度太小反而會因為過多的執行緒併發造成效能瓶頸。

圖13 分塊

分塊繪製

以下圖為例說明分塊繪製邏輯。在滑動過程中,若本地快取有此區域繪製結果,讀取快取並直接通知主執行緒展示,如例4中 X4’。否則,將該區域加入佇列,以塊為單元進行併發繪製,繪製完成後更新快取,再通知主執行緒展示,如例1中 X1’,例2中 X2‘,例3中 X3’。對劃到螢幕外的區域,從佇列中清除,終止繪製操作;若此區域已繪製完成,則通知主執行緒清除此區域的展示,如例2中 X2,例3中 X3,例4中 X4。

圖14 分塊繪製示意圖

業務應用

在完成“拼控制元件”到“畫控制元件”的思路探索與技術落地之後,需要發揮其價值,將其部署到線上進行業務實踐應用。動態佈局框架 MTFlexbox 的跨平臺程式碼複用能力對業務開發效率有了大幅提升。從產品層面看,在原有資源不變的情況下,達到了高效支撐業務迭代的效果。MTFlexbox 動態佈局框架在經歷了一次聯合共建的“洗禮”後渲染效能得到大幅提高,變得愈加成熟、完善。在過去的半年多期間,我們採用非同步渲染引擎 Graver 的 MTFlexbox 已先後應用在搜尋結果頁、美團首頁等核心流量區業務。下面列舉部分應用案例:

圖15 美團首頁、搜尋結果頁

採用非同步渲染引擎 Graver 的 MTFlexbox 絕大多數應用場景為列表級應用。如上圖所示,所有檢視卡片均為採用 M-Graver 實現的動態模板。截止到發稿,覆蓋搜尋結果頁36個動態模板,覆蓋首頁42個動態模板,業務應用累計覆蓋78個動態模板。

資料指標

以業務應用美團 App 新版首頁為例,分類頻道卡片以下全部為 MTFlexbox 實現的動態模板檢視卡片。由於採用非同步渲染引擎 Graver 的 MTFlexbox 具備了線上程安全條件下進行測量、佈局、渲染,美團首頁接入後滾動 FPS 提升明顯,對於上拉載入過程的 FPS 提升更為明顯。因此,列表使用體驗變動更加順暢。美團首頁的50分位滾動 FPS 接近59,上拉載入 FPS 接近滿幀。詳細資料如下圖所示:

圖16 優化前後 FPS 對比

總結覆盤

從業務場景作為出發點和原始驅動力,如何改善動態佈局框架的渲染效能問題,從本質上講是解決業務迭代演變時帶來的使用者體驗問題。這裡有以下幾點經驗可供大家參考:

  • 在專案設計階段要權衡考慮技術方案全景,作為技術方向規劃,不做臨時方案;架構設計要兼顧合理性、靈活性、擴充套件性。
  • 期間也會遇到諸如原生結點和繪製結點如何融合、事件處理系統怎樣建設、如何分割槽繪製等一系列問題。保持開放心態,作為探索性專案在方案細節上有很多可行性,充分討論、盯緊目標,不走極端。
  • 在跨部門協作專案中,尤其要關注專案管理、會議記錄、里程碑等,同時保持高頻的溝通。

最後,借用朱光潛先生在《藝文雜談·談對話體》中提到的一句話作為結尾:“疑難是思想的起點與核心。”

參考資料

  • 美團移動端動態化實踐
  • 深入瞭解瀏覽器的頁面渲染機制

作者簡介

  • 洋洋,美團點評資深工程師。
  • 柏泉,美團點評高階技術專家。
  • 曉宇,美團點評研發工程師。

招聘

美團外賣長期招聘 Android、iOS、FE 高階/資深工程師和技術專家,Base 北京、上海、成都,歡迎有興趣的同學投遞簡歷到[email protected]