淘特 Flutter 流暢度優化實踐 · 二期

語言: CN / TW / HK

淘特 Flutter 流暢度優化實踐 · 二期

作者:謝偉(韋聖)

在上一篇《淘特 Flutter 流暢度優化實踐》中說到,雖然一期效果較為明顯,但距離極致的使用者體驗仍有不小的差距。去年,淘特端架構聯合業務團隊共同發起“基礎鏈路極致體驗優化”的專案,目標在時長與流暢度方面獲得極致體驗,本文將為大家詳細解析淘特 Flutter 流暢度優化實踐二期部分。

優化效果

首先,我們簡單回顧上一期優化後的效果, 在一期中,主要的優化措施集中在“業務最佳實踐”,不需要改Engine、不需要造輪子,依然取得不錯的優化效果。證明Flutter在面對複雜的業務場景時, 只要掌握好足夠的實踐經驗,依然能保持較好的效能表現。

但隨著淘特的優化進入深水區,源自對極致使用者體驗的追求及Native效能資料的賽跑,讓我們開始快速從集團、業界吸收優秀前輩的經驗並自我突破。最終,在完成了“Hummer引擎升級”、“自研ExternalImage圖片庫”、“自研FlowView高效能流式容器”等多項重點技術突破後,取得的二期成果如下:

淘特 Flutter 流暢度優化實踐 · 二期

注:流暢度隨著業務迭代、測試口徑的變化存在一定波動。以上資料來自:淘特4.14.0 雙端慢滑測試口徑(滑動速率參考下方錄屏)

從體感錄屏來看有兩大提升:

  1. 流暢度再度提升,雙端慢滑基本無卡頓
  2. 消滅iOS大卡頓問題

Android錄屏對比(左邊優化前、右邊優化後)

檢視影片請點選:淘特 Flutter 流暢度優化實踐 · 二期

iOS錄屏對比(左邊優化前、右邊優化後)

檢視影片請點選:淘特 Flutter 流暢度優化實踐 · 二期

優化過程中關鍵技術

引擎升級 Hummer

相信不少同學都聽過Hummer引擎(UC Flutter定製引擎),得益於UC豐富的渲染效能優化經驗及AliFlutter社群生態,淘特在充分調研後決定接入Hummer,但同時接入的過程中,淘特又與Hummer遇見了很多複雜場景需要解決的流暢度、載入耗時、記憶體等各方面的問題。

首先升級Hummer引擎帶來的流暢度優化主要在以下4個方面,具體原理參考Hummer引擎介紹,本文側重講淘特實際解決的問題與解法,及升級的對比效果。

淘特 Flutter 流暢度優化實踐 · 二期

下面是引擎升級的最終效果:

淘特Android4.4.0基線

淘特 Flutter 流暢度優化實踐 · 二期

淘特 Flutter 流暢度優化實踐 · 二期

淘特iOS4.6.0基線

淘特 Flutter 流暢度優化實踐 · 二期

淘特 Flutter 流暢度優化實踐 · 二期

雙端在幀率、卡頓率都有了明顯的提升,尤其是iOS端解決了Feeds流在iPhone6下容易大卡頓的問題。而升級引擎對淘特影響最大的就是外接圖片庫的升級。

Flutter圖片庫對比

以流暢度為例, 圖片庫的選擇是至關重要的。由於Hummer引擎中遷移原有CDNImage(舊引擎耦合Engine的外接圖片庫方案)的成本過大,這迫使淘特尋找新的“非侵入引擎的圖片庫方案”。

最開始我們嘗試用NetworkImage拿到了第一手的體驗資料。流暢度提升非常明顯,但NetworkImage的不足只能滿足短期快速灰度,此時FImage(外接紋理的圖片庫方案)出現在我們面前,無疑是一個非常高效的解決方案,但實測後發現低端機流暢度較之前略有下滑,這讓我們開始探尋更適合淘特的圖片庫方案。

最終自研ExternalImage, 在流暢度和載入耗時對比中均取得超越CDNImage的效果。

以下是集團Flutter主要的圖片庫方案對比:

淘特 Flutter 流暢度優化實踐 · 二期

以下為Hummer下ExternalImage與外接紋理方案幀耗時效能圖,我們發現外接紋理方案的Raster執行緒幀耗時會顯著高於原生Image元件方案,原因初步分析是過多的Texture使低端機Raster耗時負載過大, 而由於frame_time近似等於max(ui,raster), 所以當raster幀耗時超越16ms也會造成實際體感FPS的降低。於是就有了下一節的ExternalImage圖片庫。

淘特 Flutter 流暢度優化實踐 · 二期

淘特ExternalImage圖片庫

淘特 Flutter 流暢度優化實踐 · 二期

上面是一張ExternalImage的總體架構圖, 其基於FFI方式載入來自Native的畫素資料,從官方Image元件出發,經Provider發起Channel呼叫,拿到圖片返回結果後觸發decode、setState等流程, 綠色代表請求鏈路,黃色代表回傳鏈路。

核心技術點如下:

淘特 Flutter 流暢度優化實踐 · 二期

淘特Flow-View輕量級流式容器

列表滾動一直是Flutter流暢度優化的重點場景,在未獲得Hummer引擎優化前, 流暢度優化一度遇到瓶頸。在Flutter官方流式容器設計理念中,在列表滾動時, Element被視為非常輕量級的元件,沒有支援複用。在列表增減元素時,Widget被認為非常輕量, 未做區域性重新整理。這在淘特實際複雜的Feeds業務場景下,受到了嚴峻挑戰。我們結合業務特性,自研了一套輕量級流式容器方案Flow-View。 主要支援2個特性:

  1. 區域性重新整理, 將使分頁載入更多無需重複build已有的itemWidget;
  2. Element/RenderObject複用, 將使滾動插入新元素時效率更高。

FlowView區域性重新整理

淘特 Flutter 流暢度優化實踐 · 二期

我們首先看上圖右側示意圖,在區域性重新整理場景下,左邊未優化前新增新元素將setState觸發整個列表Rebuild。右邊優化後將只對新增的2個元素執行插入操作,已有的元素無需Rebuild。

左側為原始碼細節,通過在SliverMultiBoxAdaptorElement.update方法中,通過滾動到底部,且newDelegate.childCount>oldDelegate判斷為載入更多場景執行區域性重新整理(即插入新的元素)。

FlowView Element、RenderObject複用

淘特 Flutter 流暢度優化實踐 · 二期

同樣先看上圖右側示意圖,在滾動場景下,當新的元素12、13即將入屏時, 左邊未優化前將建立全新的Element、RenderObject。右邊優化後新元素12、13將複用頂部移出的0、1元素的Element、RenderObject。做到迴圈利用,效率更高。

左側為原始碼細節,當獲取新插入的item時,通過在SliverMultiBoxAdaptorElement.createChild方法中,未優化前_childElements[index]=null將觸發Element、RenderObjec新建,優化後將先根據新元素的型別找是否有可複用的元素,再觸發updateChild, 若快取不為空,則會執行didUpdateWidget邏輯。

當移除元素時,removeChild將不再deactive Element(即不觸發updateChild)。同時通過修改framework將RenderObject從ContainerRenderObjectMixin雙向連結串列中移除(renderObject._removeFromChildList)。再將Element新增進cacheMap即可。

Android滑動手感

在淘特,不僅關注流暢度資料的提升,更關注使用者實際的體感。在某一次版本升級後,Android的手感不如以前順滑,滑動初期阻尼感升高明顯。如下影片對比。

1.優化前4.2.0

檢視影片請點選:淘特 Flutter 流暢度優化實踐 · 二期

2.優化後4.3.0

檢視影片請點選:淘特 Flutter 流暢度優化實踐 · 二期

分析原因是 基於BouncingScrollSimulation實現的下拉重新整理元件改變了Android平臺原有的Simulation。經過分析Android和iOS平臺的Simulation滑動演算法。

淘特 Flutter 流暢度優化實踐 · 二期

我們決定在Android上根據是否滾動到盡頭時區分Simulation演算法。在未滾動到底部前,我們仍然用ClampingScrollSimulation保持近似Android原生的手感,滾動到盡頭後為支援下拉重新整理,切換至ScrollSpringSimulation。基於此動態切換的演算法封裝了一個通用的BouncingableClampingScrollSimulation供業務使用。

淘特 Flutter 流暢度優化實踐 · 二期

總結與展望

淘特 Flutter 流暢度優化實踐 · 二期

綜上,在一期、二期的優化中,淘特Flutter流暢度優化線上下測試中取得了不錯的效果,部分頁面超過了Native。低端機也穩定在較高的幀率。但線上使用者的真實場景遠比線下複雜的多,所以淘特將在今年對以下三個方面做加大投入。

  1. 從線下走向線上,參與AliFlutter-APM共建、建設淘特全鏈路效能分析平臺。
  2. 在機型方面,淘特在從之前重點關注的低端機體驗優化走向全機型的體驗優化,這塊計劃在如滑動插值器卡頓調優、 適配iphone高刷屏、升級FlutterImageView/SurfaceTexture發力。
  3. 最後, 之前的優化工作很大一部分是人工專項優化,後續將做到更多流程自動化,如在低效能Widget告警工具、整合效能卡口工具等方面發力, 保障業務高質量低成本保持優化成果。

【參考文獻】

[1]:Google Flutter 團隊 Xiao Yu:Flutter Performance Profiling and Theory

[2]:閒魚 雲從:他把閒魚APP長列表流暢度翻了倍

[3]:Google Android RecyclerView.ViewHolder:RecyclerView.Adapter#onCreateViewHolder

[4]: Jank卡頓及stutter卡頓率說明

[5]:淘特 Flutter 流暢度優化實踐