近些年,移動端動態化技術可謂是「百花齊放」,其中的渲染性能也是動態化技術一直在探索、研究的課題。美團的開源框架 Graver 也爲解決動態化框架的渲染性能問題提供了一種新思路:關於佈局,咱們能夠採用「畫控件」方案替代傳統的「拼控件」方式。本文嘗試給出一些探索思考與實踐經驗的分享。css
動態化技術指的是不依賴程序安裝包,就能進行動態實時更新頁面的技術。特別是對於電商、社交等須要快速迭代、實時調整的強運營類業務來講,動態化具備很是重要意義。它的優點主要表現爲:提升人效、縮短迭代試錯週期、解決版本長尾問題、減小包大小等等。html
從2018年開始,移動端設備的增加紅利再也不,整個生態增加趨勢開始由高走低,與之對應的開發生態在 Native 技術方向也逐漸開始進入低迷階段,大方向在向跨平臺演進,方案上已是「百花齊放」。現有的客戶端動態化技術主要能夠劃分爲以基於 Webview 的 Web 頁面動態化加載、本地內置多個模板支持動態切換、支持動態 DSL 的佈局引擎以及基於虛擬機等四類。前端
動態化方案的渲染引擎多數是基於原生 UI 控件搭建動態化頁面。基於 Webview 的 Web 頁面動態化,實質是基於瀏覽器運行網頁,頁面繪製效率、運行效率相對較低一些。然後三種解決方案,分別經過創建映射表、佈局引擎、虛擬機與客戶端渲染引擎通信及調用關係,渲染引擎則都是基於原生 UI 控件搭建動態化頁面。因爲操做系統提供的 UI 控件佈局/繪製僅支持主線程訪問,大量原生 UI 控件操做致使 CPU/GPU 負擔太重,因此在構建複雜的動態化頁面上存在效率和性能瓶頸。所以,渲染性能是動態化技術一直在探索、研究的課題。本文嘗試給出一些探索思考與實踐經驗的分享。git
如前言部分的圖1所示,MTFlexbox 是美團點評自研的一款跨平臺動態佈局框架,它遵循了 CSS3 中提出的 Flexbox 規範來抹平多端差別。美團 App 首頁、搜索結果頁等業務有一個共同點,就是面向的業務方比較多,承載了流量輸送變現的能力。在視圖層面呈現輕交互、重展現的特徵。頻繁變更 UI,快速上線是一個剛需,MTFlexbox 正是知足了這樣一個剛需。github
因爲本文側重對 MTFlexbox 的渲染性能優化,故僅對 MTFlexbox 作歸納介紹。MTFlexbox 首先定義了一份跨平臺統一的 DSL 佈局描述文件,前端經過編輯器編輯產生布局文件並上傳到雲端,客戶端下載佈局文件而後根據佈局中的描述信息綁定業務數據,最後基於原生 UI 控件搭建視圖並渲染展現。MTFlexbox 的工做原理以下圖所示:瀏覽器
然而,隨着業務的迭代演變,美團 App 首頁、搜索結果頁等業務視圖卡片樣式愈來愈多,展現也愈來愈複雜。樣式種類多意味着視圖複用率低,極端場景下甚至沒法進行復用。展現複雜,同時也意味着控件數量多、佈局複雜、層級深。若是大量複雜操做都發生在主線程,不免形成渲染卡頓等用戶體驗方面的問題。緩存
針對上述問題,外賣終端用戶研發組、美團終端技術研發組、美團終端業務研發組合做雙贏,三方協調資源成立了跨部門、跨事業部的虛擬專項聯合項目組,三方精誠合做,在技術上不斷追求卓越,力求同時保證穩定性、動態化和高性能。安全
動態佈局框架 MTFlexbox 經過系統 UIKit 搭建視圖並渲染展現,其測量、佈局、繪製過程均發生在主線程。而做爲一款 iOS 端高效的 UI 異步渲染框架 Graver,其佈局計算、渲染過程徹底異步化,整個過程結束後才通知 UI 線程進行展現。這給咱們解決動態化框架的渲染性能問題打開了新思路:關於佈局,咱們能夠採用「畫控件」方案替代傳統的「拼控件」方式。Graver 已經在美團 App 的外賣頻道、獨立外賣 App 核心業務場景的多個業務中經歷了一年多的實踐檢驗。良好的穩定性和出色的渲染性能,也獲得了美團外賣內部技術團隊的承認和確定。關於 Graver 更多的內容這裏再也不贅述,詳細介紹請參考另外一篇技術博客:《美團開源 Graver 框架:用「雕刻」詮釋 iOS 端 UI 界面的高效渲染》 。性能優化
如何構建基於 Graver 進行異步渲染的動態化框架(MTFlexbox),成爲首先須要解決的問題。數據結構
經過對系統 UI 渲染流程分析不難發現:惟一肯定一個視圖展現僅須要肯定視圖佈局信息、內容信息、渲染信息三個要素。含義以下:
Graver 的每一個繪製元素經過 WMMutableAttributedItem 來表達內容信息、渲染信息,CGRect 表達繪製元素的大小和位置。渲染整個過程除畫板視圖外,徹底沒有使用 UIKit 控件,最終產出的結果是一張位圖(Bitmap)。若是能經過一棵樹形結構組織全部的繪製元素即繪製結點樹,便可按照遞歸遍歷的方式「畫控件」來轉義「拼控件」構建視圖。接下來,咱們須要思考如何創建 MTFlexbox 的數據結構與繪製結點樹之間的關係,而且保證該轉化過程徹底異步化。
如開篇動態佈局框架章節 MTFlexbox 的原理所描述:在相繼完成模板樹構建、數據綁定以後即進行了視圖樹構建。然而,出於功能劃分考慮、兼顧保留 MTFlexbox 的系統 UI 渲染引擎能力以及構建繪製結點樹須要的必要信息考慮,須要構建一箇中間數據結構:虛擬結點樹。它應包含樹形結構的層級信息、Flex 屬性信息、數據解析處理後的內容信息以及基本的渲染信息。虛擬結點樹是既能構建 UI 控件樹也能構建繪製結點樹的「橋樑」。
經過上述思路分析,肯定了關鍵數據結構:虛擬結點樹、繪製結點樹。接下來,咱們須要思考如何構建虛擬結點樹到繪製結點樹的數據流。
在前端有兩個重要的概念:迴流、重繪。
參考前端技術思想以及考慮單一職責原則,在虛擬結點樹與繪製結點樹中間構建 Fat 型渲染結點樹和 Thin 型渲染結點樹。Fat 型渲染結點樹負責保存原始數據以便作邏輯處理,Thin 型渲染結點樹負責保存位置、大小和內容信息。當有修改不影響幾何尺寸變化的狀況下,僅從新生成 Thin 型渲染結點樹的內容信息便可。
提升渲染性能的關鍵,便是全力保證主線程的最小資源開銷。所以,須要思考如何保證虛擬結點樹到繪製結點樹的轉換過程是線程安全的。Facebook 開源的跨平臺佈局引擎 Yoga,提供了經過 UI 視圖樹中 Flex 屬性計算得出每一個 UI 控件的位置和大小。然而,提供給 iOS 平臺的插頭類是基於 UIView 的,即佈局計算過程必須在主線程。須要基於 Yoga 核心邏輯從新封裝基於渲染結點樹的計算邏輯,以保證佈局計算是線程安全的。以下圖所示:
有了上述的思路分析,接下來咱們開始着手 Graver 接入 MTFlexbox 的架構設計。Graver 須要做爲獨立渲染引擎存在,並保留接入多種動態化框架的可能,這是出於架構設計的靈活性和擴展性的考慮。接入層命名爲 M-Graver,其上層基於 MTFlexbox 進行擴展但可靈活插拔,下層基於 Graver 渲染引擎,以下圖10所示:
M-Graver 是線程安全的,其主要分爲解析層、聚合層、佈局層和預備層。下面對各層分別作簡單的介紹:
按照上述思路分析完成架構設計,但在實施部署的過程當中也遇到了很多的技術難點和問題。如:動態佈局框架 MTFlexbox 建立至今已兩年有餘,因業務的快速發展而產生了一些技術「負債」。爲了保證不影響線上原有的業務邏輯,因此在進行 MTFlexbox 的模板樹到虛擬結點樹,再到 UI 視圖樹的技術升級改造過程時,尤爲須要關注各類「蛛絲馬跡」式的細微邏輯。
另外,在將異步渲染引擎 Graver 接入 MTFlexbox 的過程當中也遇到了諸多問題,包括如何構建基於位圖的事件處理系統,跨渲染引擎的技術融合,一些極端場景下的繪製效率瓶頸等等。下面將逐一展開闡述。
因爲視圖最終經過渲染位圖來呈現,這就須要創建基於位圖的事件處理系統。如前文所述,渲染結點樹記錄了每一個控件的位置、大小信息以及層級結構,基於此可仿照系統事件處理邏輯進行基於位圖的事件處理系統設計。在視圖展現期間,畫板視圖收到事件響應通知後(如點擊了畫板視圖中標號爲5的紅色按鈕),根據位圖對應的渲染結點樹存儲的各控件佈局、層級和渲染信息,逐層遍歷找到須要響應的渲染結點,若是涉及信息修改則變動其在渲染結點樹中的渲染信息,觸發再次渲染的同時執行該渲染結點綁定的事件方法。遍歷渲染結點樹的輸入是系統基於畫板視圖返回的點擊位置,其遍歷過程與系統 UI 事件查找過程比較類似。事件處理過程以下圖11所示:
從美團業務特徵出發,圖文組合佔據多數 UI 場景。然而也存在諸如動效等沒法依託 Graver 進行圖文渲染的狀況。所以,須要考慮跨渲染引擎的渲染融合問題。在 M-Graver 的預備層遍歷渲染結點樹時,能夠根據當前結點是否爲原生結點決議樹拆分,若是是原生結點,將該結點連同其子樹「直系」繪製結點從渲染結點樹中拆分下來,以該結點爲根結點的子渲染結點樹,生成對應的繪製結點樹,多個子渲染結點樹的根結點,構成了以畫板視圖爲單元的畫板視圖樹。以下圖12所示:
爲了便於理解,咱們給出如下幾個名詞的解釋說明:
樹拆分的過程還涉及到兄弟結點層級顛倒以及佈局交叉等問題。兄弟結點層級顛倒問題經過結點變異來解決。佈局交叉問題存在於斷定渲染結點樹的結點是繪製結點或原生結點以前,因爲佈局緣由存在視圖交叉。佈局交叉問題經過新建畫板視圖插入來保證層級正確以及繪製正確。因爲篇幅有限,這裏再也不贅述。
從產品交互層面看,爲了提升屏效每每存在多向滑動的視圖組件場景。如橫滑 Scroll 組件,其特色是須要經過滑動才能逐漸看到全部的視圖內容。經過異步渲染繪製位圖來實現的狀況下,存在單一併發渲染任務計算邏輯繁重的問題,從用戶體驗層面看容易形成「白屏」現象。爲解決該問題,將視圖卡片渲染過程分解,進行增量渲染,採用漸進式的方式減小空白頁面等待時間。根據待展現區域在屏幕中相對位置進行區塊劃分,經過隊列集中控制繪製操做。以此進一步提升併發效率,並減小渲染過程的非必要系統資源消耗。
區塊劃分
區塊劃分策略的實質是繪製結點樹的拆分,將繪製結點樹中不存在佈局交叉的子結點樹進行逐一拆分,每一個拆分下來的繪製結點子樹即爲一個區塊,同時要設置最小塊策略,不然拆分粒度過小反而會由於過多的線程併發形成性能瓶頸。
分塊繪製
如下圖爲例說明分塊繪製邏輯。在滑動過程當中,若本地緩存有此區域繪製結果,讀取緩存並直接通知主線程展現,如例4中 X4'。不然,將該區域加入隊列,以塊爲單元進行併發繪製,繪製完成後更新緩存,再通知主線程展現,如例1中 X1’,例2中 X2‘,例3中 X3’。對劃到屏幕外的區域,從隊列中清除,終止繪製操做;若此區域已繪製完成,則通知主線程清除此區域的展現,如例2中 X2,例3中 X3,例4中 X4。
在完成「拼控件」到「畫控件」的思路探索與技術落地以後,須要發揮其價值,將其部署到線上進行業務實踐應用。動態佈局框架 MTFlexbox 的跨平臺代碼複用能力對業務開發效率有了大幅提高。從產品層面看,在原有資源不變的狀況下,達到了高效支撐業務迭代的效果。MTFlexbox 動態佈局框架在經歷了一次聯合共建的「洗禮」後渲染性能獲得大幅提升,變得越發成熟、完善。在過去的半年多期間,咱們採用異步渲染引擎 Graver 的 MTFlexbox 已前後應用在搜索結果頁、美團首頁等核心流量區業務。下面列舉部分應用案例:
採用異步渲染引擎 Graver 的 MTFlexbox 絕大多數應用場景爲列表級應用。如上圖所示,全部視圖卡片均爲採用 M-Graver 實現的動態模板。截止到發稿,覆蓋搜索結果頁36個動態模板,覆蓋首頁42個動態模板,業務應用累計覆蓋78個動態模板。
以業務應用美團 App 新版首頁爲例,分類頻道卡片如下所有爲 MTFlexbox 實現的動態模板視圖卡片。因爲採用異步渲染引擎 Graver 的 MTFlexbox 具有了在線程安全條件下進行測量、佈局、渲染,美團首頁接入後滾動 FPS 提高明顯,對於上拉加載過程的 FPS 提高更爲明顯。所以,列表使用體驗變更更加順暢。美團首頁的50分位滾動 FPS 接近59,上拉加載 FPS 接近滿幀。詳細數據以下圖所示:
從業務場景做爲出發點和原始驅動力,如何改善動態佈局框架的渲染性能問題,從本質上講是解決業務迭代演變時帶來的用戶體驗問題。這裏有如下幾點經驗可供你們參考:
最後,借用朱光潛先生在《藝文雜談·談對話體》中提到的一句話做爲結尾:「疑難是思想的起點與核心。」
美團外賣長期招聘 Android、iOS、FE 高級/資深工程師和技術專家,Base 北京、上海、成都,歡迎有興趣的同窗投遞簡歷到duyao04@meituan.com。