開發與維運

他把閒魚APP長列表流暢度翻了倍(良心教程)

作者:閒魚技術-雲從

1 整體思路

閒魚在業務的快速迭代過程中,app 的長列表滑動流暢度逐步惡化,對用戶瀏覽內容體驗產生傷害。閒魚作為國內 flutter 應用的先驅,APP 以 flutter 和原生 Native 的混合工程存在。這裡分別就 Android 原生、flutter 頁面和大家分享我們的優化思路。

本文分為三個部分:

  • 流暢度指標和檢測工具構建
  • 原生 Android 長列表優化
  • flutter 長列表優化

流暢度優化整體思路圖如下:

2 流暢度指標和檢測工具構建

2.1 現狀和難點

檢測工具現狀:以 Android 為例,現有流暢度工具可分為:

  • 侵入式

    • 集成 sdk,通過註冊幀回調計算流暢度。Android 見 Choreographer 類
    • profile 模式
  • 無侵入式

    • 執行系統命令,如 adb shell dumpsys gfxinfo ${packageName}
    • 騰訊 GT APP,底層執行 service call SurfaceFlinger 1013,高版本 Android 已不支持

流暢度指標現狀有:

  • FPS (Frames Per Second)
  • SF(SkippedFrame,跳幀)
  1. 在單位時間 1 秒內,跳過執行 Choreographer 中 doFrame() 的次數 (見 《移動App性能評測與優化》
  • SM(Smooth,流暢度)
  1. 在單位時間 1 秒內,實際執行 Choreographer 中 doFrame() 的次數。其中 SM=60-SF。(見 《移動App性能評測與優化》
  • 幀耗時數據
    使用 adb 命令得到幾個關鍵分位的幀平均耗時:
Total frames rendered: 2245
Janky frames: 31 (1.38%)
50th percentile: 5ms
90th percentile: 10ms
95th percentile: 14ms
99th percentile: 18ms

然而以上工具和指標定義在 app 的複雜場景下,尚存在問題

  1. 多平臺問題
    現 APP 技術有原生、h5、小程序、RN、weex、flutter 等。暫無一款無侵入的流暢度檢測工具能同時支持多個平臺、多種機型和多個指標數據,而侵入式的檢測工具無法檢測競品 APP。
  2. 指標選擇和用戶體驗一致性
    我們期望能有少量的幾個指標數據,準確的表達用戶流暢度體感。

平均 FPS(SM 和 SF 類似),不足以反映用戶體驗。如相同 30 FPS,可以是 1s 內 30 個 33.3 ms的畫面,也可以是 29 個 16.6ms 的畫面再加 1 個 516.9 ms 的畫面,但用戶體驗並不相同。

  1. 流暢度數據影響因素多
    滑動速度和滑動狀態:idle(停止)、drag(手指拖拽)、fling(自由滑動)都是影響流暢度數據的重要因素。

2.2 流暢度指標制定

維基百科 動畫定義:一種通過定時拍攝一系列多個靜止的固態圖像(幀)以一定頻率連續變化、運動(播放)的速度(如每秒16張)而導致肉眼的視覺殘象產生的錯覺——而誤以為圖畫或物體(畫面)活動的作品及其影片技術。

列表滑動同理,是 APP 以一定頻率(60hz下16.6ms)和不同 offset 計算出一系列靜止畫面,讓肉眼看到滑動動畫。

當我們說列表滑動不流暢,是因為頻率過低無法讓肉眼產生視覺殘留,或在時間(畫面停留時長)和空間(畫面內容)產生跳變,讓用戶感知到變化的不自然。以此我們可以定義指標如下:

  • 時間角度

    • 定義平均 FPS:定義一次檢測的平均幀率。反應畫面平均停留時長。
    • 定義 1s 大卡頓次數:平均 1s 內出現佔用 3 幀及以上的畫面次數。反應畫面停留時長跳變
  • 空間角度

    • offset 跳變值:在畫面不掉幀的情況,若其中一個畫面出現跳變,甚至花屏或者綠屏會讓用戶體驗到不流暢。在 APP 滑動過程中,畫面內容由 offset 決定,而 offset 跳變,和卡頓時長、差值器實現均有關聯,現有差值器實現基本基於 D/T 曲線(距離/時間),為此平均 FPS 和 1s 大卡頓次數很大程度上體現了畫面跳變,同時考慮到無侵入式檢測 offset 的難度問題,暫不考慮 offset 跳變值。

綜上,我們定義流暢度指標為平均 FPS 值和 1s 大卡頓次數。

2.3 流暢度檢測工具實現

我們從 APP 錄屏畫面入手,計算流暢度指標值。當我們得到 APP 滑動過程中的錄屏數據,可通過每 16.6ms 檢測錄屏畫面是否發生變化,當連續畫面未發生變化,則表示發生了卡頓。無變化的連續畫面數則表示了卡頓的時長。
image.png

為得到目標 APP 錄屏數據,檢測工具 APP 向系統註冊錄屏服務,然後在檢測工具 APP 的幀回調中不停讀取錄屏畫面,並和上次檢測畫面 hash 值進行比對。

  • 檢測工具 APP 和目標 APP 進程隔離,為此目標 APP 發生卡頓並不影響檢測工具 APP 的幀回調
  • 為保證每次錄屏畫面讀取和 hash 值計算在 16.6ms 內完成,需根據高低端機型調整畫面寬高壓縮比。

為排除滑動操作對流暢度數值的干擾,我們使用腳本操作檢測工具 APP 和目標 APP 的滑動。自動化腳本原理為使用 adb 命令操作手機

點擊:adb shell input tap $x $y
滑動:adb shell input swipe $x1 $y1 $x2 $y2 $duration

2.4 檢測工具演示

流暢度檢測工具 APP 以懸浮框的方式顯示,下面為目標檢測 APP:

image.png

流暢度檢測工具界面

2.5 小結和展望

在流暢度指標方面,我們定義了平均 FPS 和 1s 大卡頓次數作為指標,更好的反應了用戶體驗。
在流暢度檢測工具方面,我們實現了無侵入檢測工具,支持以下特性:

  • 無侵入
  • 支持檢測第三方 app
  • 支持多平臺:native,flutter,h5,小程序
  • 多維度數據:平均 FPS,平均 1s 大卡幀次數,幀分佈直方圖,幀分佈均方差
  • 自動操作,避免人為操作差異

此外,流暢度檢測工具還有一些不足之處

  • 列表中有視頻卡片

    • 停止滑動時,若列表中有視頻播放,由於畫面一直在變化,檢測工具無法判斷是滑動停止;同時,由於視頻 fps 值為 30 左右,會導致流暢度數據偏低
    • 如何避免:檢測過程中,需保證列表滑動不停止
  • 低端機(y67)真實 fps 計算存在偏差

    • 為保證低端機上(如 vivo y67)上計算大圖像 hash 值在 16ms 以內,錄屏畫面壓縮較大(寬度壓縮 100,高度壓縮 10),為此在大量空白或者大色塊的場景下,無法檢測到畫面的細微變化,fps 計算存在偏低。
    • 如何避免:避免低端機上檢測大量空白或大色塊的場景

3 原生 Android 長列表優化

Android 原生長列表優化已經非常成熟了,在工具方面有 traceview、blockcanary、DDMS、Android Profile 等。常見優化手段也很多:佈局層級優化,過度渲染優化,頻繁measure、layout優化,UI 線程耗時方法優化、冗餘資源資源加載優化等,這裡不再贅述。

除此之外閒魚使用以下 2 點優化首頁

3.1 異步構建視圖緩存池

通過工具檢測或耗時打印,發現列表初始滑動和 loadmore 時觸發 item 視圖構建耗時嚴重(RecyclerView.onCreateViewHolder)
image.png

查看首頁顯示和初始滑動流程,可以發現流程中其他 UI 操作過程和等待用戶操作過程均有優化空間。

利用 AsyncLayoutInflater 原理異步構建視圖緩存池,優化首頁列表流程如下:

其中視圖緩存池構建完成的時機在不同機型下不同,可能在列表首屏多卡片構建之前,或構建中,或在用戶滑動操作之前完成,或一開始構建就拋出錯誤停止構建

注意:不能直接使用 AsyncLayoutInflater,AsyncLayoutInflater 在異步構建失敗後有一個降級到 UI 線程構建的邏輯,為避免降級邏輯發生導致緩存池在 UI 線程構建,導致頁面更加卡頓,需要移除這個降級邏輯:出現異步 inflater 失敗,停止緩存池構建。

3.2 ViewDataUnbinder 快速抽離 UI 操作

在卡片數據綁定階段(RecyclerView.onBindViewHolder),在低端機上耗時較為嚴重,原因是在卡片數據綁定方法中,而 UI 和非 UI 操作糅合在一起,由於 UI 邏輯必須在 UI 線程執行,最終導致全部邏輯只能在 UI 線程執行。

能想到定義視圖數據層,將 UI 和非 UI 操作分離開,然而實際編碼發現業務代碼改動量大且容易出錯,AB 測試邏輯難以實現。那有沒有更好的方案,用最少量代碼抽離 UI 操作呢?

核心思路:編譯期根據視圖類自動生成 ViewData 類,並替換視圖類實例。ViewData 類和視圖類擁有相同的關鍵方法簽名,方法執行時記錄視圖操作,統一切換到 UI 線程執行視圖操作。

具體使用代碼樣例如下

  1. 註解視圖類
    使用 ViewDataAnno 註解視圖類,UIMethodAnno 註解 UI 操作方法。

 image.png其中註解說明
image.png

  1. 生成 ViewData 類

image.png

  1. 業務代碼修改

    • 修改視圖變量為 ViewData 類型
    • 原視圖數據綁定邏輯放置後臺線程

3.3 優化結果

trim (1).gif

閒魚首頁,在恢復內容上屏速度(流暢度降低)後提升流暢度

4 Flutter 複雜長列表優化

flutter 一直以高性能被大家所認知,參考 Flutter 是如何做到性能直逼 native 的?,這也是閒魚當初選擇 flutter 的一個重要原因。而在閒魚的實際 flutter 頁面,如商品詳情頁和搜索結果頁,長列表滑動流暢度體驗卻不盡人意。

4.1 工具使用和常見優化

做性能優化前,需要理解 flutter 的渲染原理,如 Widget、Element、RenderObject 三棵樹結構、Widget 到屏幕顯示過程等,可參考 超詳解析Flutter渲染引擎複雜業務如何保證Flutter的高性能高流暢度?

針對性能問題,首推官方性能分析工具並結合使用 profile 模式查看性能問題,參考 Flutter Performance 分析工具簡介

Profile 模式只能在真機上運行,不能在模擬器上運行:基本和 Release 模式一致,除了啟用了服務擴展和 tracing,以及一些為了最低限度支持 tracing 運行的東西(比如可以連接 observatory 到進程)。命令 flutter run --profile 就是以這種模式運行的,通過 sky/tools/gn --android --runtime-mode=profile 或者 sky/tools/gn --ios --runtime-mode=profile 來 build。因為模擬器不能代表真實場景,所以不能在模擬器上運行
引自:Flutter性能調優、複雜業務保證Flutter的高性能高流暢

4.1.1 檢查 widget rebuild 情況

Android Studio 上 ViewTool WindowsFlutter Performance 打開檢測 Widget rebuild 情況,可以發現 FDButtonBar 被頻繁重建,然而查看視圖內容並沒有發生變化。查看代碼定位到 reducer.dart 中會根據滑動事件更新 state 中的 scrollPercent,進而產生重建。而在詳情頁中,scrollPercent 在 Widget 構建中並未參與使用。

閒魚頁面中使用了 fish-redux,在 reducer.dart 的方法中返回不同的 state 對象則表示需要重建 widget

// reducer.dart
// 滑動事件監聽
  static BottomBarState onScroll(BottomBarState state, Action action) {
    ...
    return state.clone()..scrollPercent = scrollPercent;
    ...
  }

image.png

4.1.2 使用 fish-redux 性能日誌

fish-redux 是閒魚研發一套在 flutter 上的 redux 框架,閒魚 APP 中有廣泛應用。fish-redux 中自帶性能日誌,源碼查看 performance.dart,若需要打印 profile 或 release 模式下的性能日誌,可自行修改源碼。

閒魚詳情頁滑動時,查看 adb 日誌,可以發現大量的滑動廣播通知,且存在耗時 1ms 以上事件處理。

11-15 15:03:43.684 27076 27271 I flutter : CommonBuyDetailPage performance: ItemBodyAction.onScrollBroadcast 261
11-15 15:03:43.701 27076 27271 I flutter : CommonBuyDetailPage performance: ItemBodyAction.onScrollBroadcast 1933
11-15 15:03:43.716 27076 27271 I flutter : CommonBuyDetailPage performance: ItemBodyAction.onScrollBroadcast 371

profile 模式下時間日誌

image.png
因為詳情頁中存在視圖間聯動,如標題欄的顯示隱藏漸變,問賣家的顯示消失均需要根據滑動事件做判斷。結合業務邏輯,可以發現,除了問賣家外,其他視圖在滑動超出 600 之後,收到滑動事件後不會發生視圖內容變化;而問賣家在滑動超出更大的一個值後會永遠消失不顯示,在一開始未超出這個值時,僅需要判斷滑動方向即可。基於以上業務背景,在滑動超出 600 後,若問賣家是不再顯示狀態,則不發送滑動事件;否則僅在開始滑動的 30 距離內發送事件。

此外,可以利用 fish-redux 的特性:若 reducer.dart 中返回新的 state 對象則表示 widget 重建,檢查全部的 reducer.dart 文件內方法實現,排查可能發生的無效 widget 重建。

4.1.3 優化 ClipPath 和 ClipRPath

使用 Timeline 查看渲染線程性能消耗,可以發現有多個 ClipRectLayerClipRRectLayer

image.png
打開 Debug flag debugDisableClipLayersdebugDisablePhysicalShapeLayers 重新檢查視圖,可以發現部分 ClipRectLayer 是因為圖片內容超出視圖邊界產生,部分 ClipRRectLayer 是因為卡片 Widget 圓角設置以及基於外接紋理的圖片控件裡設置了 ClipRRect 設置(即便 radius 為0也會設置)

理解原理後,我們對閒魚圖片控件新增參數,支持圖片內容圓角設置和圖片內容寬高裁剪,使 native 層生成的 Bitmap 已經滿足圓角和寬高比要求。同時修復 radius 為0也會設置 ClipRRect 的問題。優化後的 Timeline 圖如下:

4.1.4 其他優化建議

flutter 性能優化相關的優秀文章很多,本文不再對類似的排查和優化手段做贅述,這裡做下簡單彙總:

  • widget build 優化

    1. setState 狀態刷新位置儘量放置於視圖樹的低層級
    2. Provider 中獲取 Model 的方式會影響刷新範圍。推薦使用 Selector 或 Consumer 來獲取祖先 Model,以維持最小刷新範圍
    3. 對於長列表,避免使用 ListView() 構造函數,推薦使用 ListView.builder 構造函數
    4. reducer 中,state 對象中的視圖數據真正發生變化的時候,新建 state 對象
  • 主 isolate 優化

    1. 減少或延遲 widget build 中非視圖邏輯,如曝光埋點延遲到滑動停止聚合觸發
    2. 列表 Item 高度可知的情況下,推薦設置 itemExtent,減少滑動中頻繁計算列表高度
    3. 使用 const 修飾無需變更的 widget 或普通對象
    4. 使用 AnimatedBuilder 時,避免在不依賴於動畫的 widget 的構造方法中構建 widget 樹。動畫的每次變動都會重建這個 widget 樹。而應該構建子樹的那一部分,並將其作為 child 傳遞給 AnimatedBuilder
    5. 避免在動畫中剪裁。如果可能,請在動畫開始之前預先剪切圖像
  • Render 線程優化

    1. 對於頻繁更新的控件(如動畫),使用 RepaintBoundary 隔離它,創建單獨 layer 減少重繪區域
    2. 使用圖片替換半透明效果
    3. 減少 saveLayer(ShaderMask、ColorFilter、Text Overflow)、clipPath的使用,提升 render 線程性能
    4. 避免使用 Opacity widget,尤其是在動畫中避免使用。請用 AnimatedOpacity 或 FadeInImage 進行代替
    5. 避免使用帶換行符的長文本
  • 工具推薦

    1. 官方 DevTools 工具
    2. 利用 Debug flags 排查問題(推薦 Flutter Performance 分析工具簡介
    3. 善於利用框架日誌,如 fish-redux 性能日誌

4.2 列表 element 複用優化

flutter 列表控件劃分為可視區域和 Cache 區域,往下滑動時 element 從底部被創建進入底部 Cache 區域後,再進入可視區域,再進去頂部 Cache 區域,最後被銷燬。往上滑動邏輯類似。在不使用 keepAlive 的情況下,來回滑動,曾經創建過的 element 需要重新創建。而在我們的業務中,列表 item Widget 結構是接近的,此時如果能根據類型複用 element,就能一定程度的提升性能。

列表控件源碼見 sliver_list.dart 中 RenderSliverList.performLayout()
element 緩存在 _childElements 數組中,以 index 為索引。源碼見 sliver.dart
若 item Widget 結構差異很大,即便複用了 element,Element.updateChild 方法內部最終還是執行了 inflateWidget 方法,對於性能提升就沒什麼價值了

我們構建 index${widget.key}List<element> 的映射關係:在 widget 創建處建立 index${widget.key} 映射,在 element 應該被銷燬移除的邏輯處,將 element 緩存至 ${widget.key} 映射的 List<element> 處(注意 renderObject 對象需要從父節點移除)。列表滑動過程中,優先根據映射關係找到緩存中的 element 並使用(注意更新 element.renderObject.parentData 中的 index 值)

4.3 複雜 Widget 分幀上屏

以上全部優化手段嘗試後,在閒魚的詳情頁和搜索頁上還是遠沒有達到預期。原因是猜你喜歡卡片和搜索頁卡片本身就足夠複雜,另外由於我們引入 DX 技術讓 Widget 進一步變得巨大,最終導致的結果是:即便高端機,也無法在一幀時間內完成渲染。
然而拋開技術視角,從業務視角看,卡片展現內容和 DX 的動態能力都是必需的。那如何在滿足業務訴求的情況下,實現超大 Widget 的高性能呢?

業務側僅需 Text,但在 DX 技術中使用的是 DXTextWidget

猜你喜歡卡片在 紅米 K30Pro(CPU 驍龍 865)的 Timeline 圖

image.png

搜索結果卡片 Timeline 圖,補充了 performLayout、updateChild、Widget build

在已知常見優化手段無法滿足的情況下,我們迴歸 GUI 系統性能優化的起點去思考問題。流暢度優化思路,大體可以分為 3 個方向:

  1. 多線程方案
    在 Android 原生開發中很常見。但在 dart 世界中,不同線程(isolate)的內存是隔離的,此外由於 flutter 渲染流程三棵樹,我們不好直接操作 RenderObject,多線程方案在 flutter 中較難實施(排除 IO 更新數據後顯示等常規場景)
  2. 優化每個任務,擠壓 CPU 運算量,保證一幀時間(16.6 ms)完成任務
  3. 中的主流優化思路,前面的優化手段都是這個思路
  4. 快速響應用戶,讓用戶覺得夠快,不阻塞用戶的交互。即一幀時間內還有任務沒有完成,則停止執行,保證列表先執行滑動,未執行任務在後續幀時間片上執行
    參考 React Fiber 框架,基於時間分片的思路,協調階段將一顆任務樹轉為一條任務鏈(parent 節點 → child 節點 → sibling 節點 → parent 節點),滿足了任務鏈可中斷執行,提前提交渲染,最後實現了將一條任務鏈拆解到多幀時間分片中消化。

排除方向 1、2 後,只剩下方向 3。再結合猜你喜歡卡片 Timeline 圖可以發現,在卡片 Widget 創建的一幀發生時間不足,而後面的幾幀內時間消耗都遠沒到 16.6 ms,可以想到方向 3 是正確的。那剩下的關鍵問題僅有以下 2 點:

  1. 能否將一個大 Widget build 任務為拆分多個小 Widget build 任務並大致平均的分配到多個時間分片上?
  2. 一個大 widget 分時間片上屏是否會影響體驗?

Timeline 上任務耗時圖

Flutter widget 拆分和分幀上屏

基於時間分片的大方向,我們把一個大 widget 拆分為一個空白框架和 2 個卡片 widget,再將卡片 widget 拆分為一個卡片框架和多個 FXImage Widget,Widget 框架中不立馬顯示的部分使用佔位 Widget 臨時代替。
由此構建一個高優大任務隊列和一個低優小任務隊列,高優大任務隊列中的任務高優執行且獨佔一幀時間,低優小任務隊列低優執行且一幀時間最多能執行 12 個任務。再利用 flutter 逐步標髒,將 build 任務延遲到後續時間分片上。

以上最終將一個超大 widget 構建從 1 幀時間分散到 4 幀時間內消化,優化了卡頓。
image.png

優化後猜你喜歡卡片 Timeline 圖(紅米 K30Pro,CPU 驍龍 865)

在體驗方面,前面講列表控件結構時已知有一個不可見的 Cache 區域,所以分幀上屏大部分是在這個不可見區域完成的,為此在高端機或正常滑動情況下用戶並無感知。而在低端機上快速滑動能明顯看到卡片空白情況,但整體相比嚴重頓挫體感要好。

4.4 優化數據

基於上面的優化手段,閒魚詳情頁和搜索頁流暢度 FPS 提升了 3 個點,低端機大卡頓次數降低一半,中高端機型上流暢度提升到 57 或以上,大卡頓次數接近 0。

詳情頁線上高可用 fps 數據如下:

線上低端機 fps 曲線。綠色為優化版本
曲線分佈越靠右,流暢度越好

線上高端機 fps 曲線。綠色為優化版本

搜索頁線上高可用 fps 數據如下:

線上低端機 fps 曲線。綠色為優化版本

線上高端機 fps 曲線。綠色為優化版本

4.5 滑動差值器優化

完成上面優化後,線下自建流暢度檢測工具數據和線上 fps 數據曲線都有很大的提升,且數據指標接近原生 APP 流暢度。在中高端機型上,閒魚詳情頁 FPS 已經被我們優化到了 57 及以上了,1s 大卡頓次數接近 0。在原生 APP 流暢度 FPS 數值達到 57 及以上時,滑動過程中基本上不會感受到卡頓,然而,flutter 頁面的實際滑動操作中,還是能感受到卡頓。

回顧自建流暢度檢測工具原理:基於每幀畫面比對、無侵入,相同的自動化腳本,所以相信我們線下測試的數據(平均 FPS 和 1s 大卡頓次數)是準確的。性能數據接近,而體感有差異,且性能數據準確可信,所以可以確認流暢度指標(平均 FPS 和 1s 大卡頓次數)還不能完全反應體感。

再回顧 2.2 流暢度指標制定,可以發現我們並沒有對空間維度的 offset 跳變(畫面內容跳變)做檢測。基於此,我們可以對比 Android 原生 RecyclerView 和 Flutter SliverList 在卡頓情況下 offset 變化情況

Android 原生 RecyclerView 和 Flutter SliverList fling 階段 offset/time 曲線圖

由上可以得到,同樣在 FPS 值達到 57,Android RecyclerView 在用戶體感上比 flutter 列表控件更好的原因:在小卡頓時,offset 偏移值並沒有發生翻倍跳變。

查看 flutter 滑動算法,可以發現是基於一條 D/T 曲線計算滑動距離,所以發生卡頓時,輸入 timeOffset 值發生翻倍,最終計算出來的 offset 值發生近乎翻倍。

flutter ClampingScrollSimulation D/T 曲線

為消除在發生小卡頓時,offset 跳變的情況,我們自定義了 physics 和 simulation,在 time 發生發生小跳變時,修改滑動距離算法,採用 V/T 曲線算法,distance 通過累加的方式計算,優化了 time offset 發生翻倍而導致曲線跳變的情況

distance = velocity(time) * 16.6ms + distance

注意:需要適配系統頻率大於 60 hz 的機型(如 90hz,120hz),在一幀時間內有可能計算多次 distance

以 V/T 曲線為基礎,我們提供了以下滑動差值器:

  • SmoothClampingScrollPhysics
    無回彈差值器,停頓後偏移值不跳變。 結束滑動的效果同 ClampingScrollSimulation

  • SmoothBouncingScrollPhysics
    回彈差值器,停頓後偏移值不跳變

5 總結和展望

經過上述優化,在原生 Android 方面,閒魚首頁流暢度和內容上屏得到明顯提升;在 Flutter 方面,閒魚詳情頁和搜索頁流暢度 FPS 提升了 3 個點,低端機大卡頓次數降低一半,中高端機型上流暢度提升到 57 或以上,大卡頓次數接近 0,相同小卡頓在體驗上得到了提升。

流暢度優化是每一個 GUI 系統都一直在努力的事情,有很多優秀的工具介紹、官方和非官方的優化文章。這次優化過程中,我們也借鑑了很多別人的文章,發現和優化了一些問題,但本文儘量不去重複描述,推薦讀者閱讀相關優化文章或官方文檔。
在以上優化手段尚無法實現最終目標時,我們也做了一些不一樣的優化,期望能拋磚引玉,對讀者有所幫助和啟發:

  • 基於用戶體驗為導向構建了流暢度指標:平均 FPS,1s 大卡頓次數
  • 針對指標,自建了流暢度檢測工具,支持無侵入、跨平臺、自動化
  • [Android] 顯示 ViewDataUnbinder 組件在複雜業務邏輯中快速抽離 UI 操作
  • [Flutter] 修改 Flutter engine 源碼,支持列表 element 複用
  • [Flutter] 實現大 Widget 分幀上屏組件
  • [Flutter] 差值器算法優化

後續我們會繼續思考以下內容:

  • 如何將流暢度檢測工具內部產品化,支持非研發同事使用?
  • 如何使用已有的經驗、工具、組件快速優化其他業務頁面?
  • 如何在研發階段及時發現和防止無效 rebuild 等問題?
  • 如何在 CI 平臺及時發現頁面流暢度惡化情況?
  • 如何以業務無侵入的方式實現業務大 Widget 自動且合理地分幀上屏?

Leave a Reply

Your email address will not be published. Required fields are marked *