走近科學,探究阿里閒魚團隊透過資料提升Flutter體驗的真相

閒魚技術發表於2019-04-25

背景

閒魚客戶端的Flutter頁面已經服務上億級使用者,因此使用者體驗尤其重要,完善Flutter效能穩定性監控體系,以便及早發現線上效能問題,也可以作為使用者體驗提升的衡量標準。

那麼Flutter的效能到底如何,是否像官方宣傳的那麼絲滑?Native的效能指標是否可以用來檢測Flutter頁面?下面給大家分享我們在實踐中總結出來的Flutter的效能穩定性監控方案。

目標

過度的丟幀從視覺上會出現卡頓現象,體現在使用者滑動操作不流暢;頁面載入耗時過長容易中斷操作流程;Flutter部分exception會導致發生異常程式碼後面的邏輯沒有走到從而造成邏輯bug甚至白屏。這些問題很容易考驗使用者耐心,引起使用者反感。

所以我們制定以下三個指標作為線上Flutter效能穩定性標準:

  1. 頁面滑動流暢度

  2. 頁面載入耗時(首屏時長+可互動時長)

  3. Exception率

最終目標是讓這些資料指標驅動Flutter使用者體驗升級。

頁面滑動流暢度

我們先大概瞭解下螢幕渲染流程:CPU先把UI物件轉變GPU可以識別的資訊儲存進displaylist列表,GPU執行繪圖指令來執行displaylist,取出相應的圖元資訊,進行柵格化渲染,顯示到螢幕上,這樣一個迴圈的過程實現螢幕重新整理。

閒魚客戶端採用的Native、Flutter混合技術方案,Native頁面FPS監控採用集團高可用方案,Flutter頁面是否可以直接採用這套方案監控?

普遍的FPS檢測方案Android端採用的是Choreographer.FrameCallBack,IOS採用的是CADisplayLink註冊的回撥,原理是類似的,在每次發出Vsync訊號,並且CPU開始計算的時候執行到對應的回撥,這個時候表示螢幕開始一次重新整理,計算固定時間內螢幕渲染次數來得到fps。(這種方式只能檢測到CPU卡頓,對於GPU的卡頓是無法監控到的)。由於這兩種方法都是在主執行緒做檢測處理,而Flutter的螢幕繪製是在UI TaskRunner中進行,真正的渲染操作是在GPU TaskRunner中,關於詳細的Flutter執行緒問題可以參考閒魚之前的文章:深入理解Flutter引擎執行緒模式。

這裡我們得出結論:Native的FPS檢測方法並不適用於Flutter。

Flutter官方給我們提供了 Performance Overlay (具體參考 Flutter performance profiling) 作為檢測幀率工具,可否直接拿來用?

走近科學,探究阿里閒魚團隊透過資料提升Flutter體驗的真相

上圖顯示了Performance Overlay模式下的幀率統計,可以看到,Flutter分開計算GPU 和UI TaskRunner。UI Task Runner被Flutter Engine用於執行Dart root isolate程式碼,GPU Task Runner被用於執行裝置GPU的相關呼叫。透過對Flutter engine原始碼分析,UI frame time是執行window.onBeginFrame所花費的總時間。GPU frame time是處理CPU命令轉換為GPU命令併傳送給GPU所花費的時間。

這種方式只能在debug和profile模式下開啟,沒有辦法作為線上版本的fps統計。但是我們可以透過這種方式獲得啟發,透過監聽Flutter頁面重新整理回撥方法handleBeginFrame()、handleDrawFrame()來計算實際FPS。

01

具體實現方式

註冊WidgetsFlutterBinding監聽頁面重新整理回撥handleBeginFrame()、handleDrawFrame()

handleBeginFrame: Called by the engine to prepare the framework to produce a new frame.
handleDrawFrame: Called by the engine to produce a new frame.

透過計算handleBeginFrame和handleDrawFrame之間的時間間隔計算幀率,主要流程如下圖:

走近科學,探究阿里閒魚團隊透過資料提升Flutter體驗的真相


02

效果

到這裡,我們完成Flutter中頁面幀率的統計,這種方式統計的是UI TaskRunner中的CPU操作耗時,GPU操作在Flutter引擎內部實現,要修改引擎來監控完整的渲染耗時,我們目前大部分的場景沒有複雜到gpu卡頓,問題主要還是集中在CPU,所以說可以反應出大部分問題。從線上資料來看,release模式下Flutter的流暢度還是蠻不錯的,ios的主要頁面均值基本維持在50fps以上,android相對ios略低。這裡需要注意的是幀率的均值fps在反覆滑動過程中會有一個稀釋效果,導致一些卡頓問題沒有暴露出來,所以除了fps均值,需要綜合掉幀範圍、卡頓秒數、滑動時長等資料才能反應出頁面流暢度情況。

頁面載入時長

01

Native和Weex頁面載入演算法對比

集團內部高可用方案統計Native頁面載入時長是透過容器初始化後開啟定時器在容器layout的時候檢查螢幕渲染程度,計算可見元件的螢幕覆蓋率,滿足條件水平>60%,垂直>80%以上認為滿足頁面填充程度,再檢查主執行緒心跳判斷是否載入完成。

走近科學,探究阿里閒魚團隊透過資料提升Flutter體驗的真相

再來看看weex頁面載入流程和統計資料的定義,

走近科學,探究阿里閒魚團隊透過資料提升Flutter體驗的真相

Weex的頁面重新整理穩定定義:螢幕內view渲染完成且view樹穩定的時間。

具體實現:當螢幕內發生view的add/rm操作時,認為是可互動點,記錄資料。直到沒有再發生為止。

走近科學,探究阿里閒魚團隊透過資料提升Flutter體驗的真相

在概念上Flutter和weex的首屏時長和可互動時長並不完全一致,Flutter之所以選擇從路由跳轉開始計算時長主要是因為這種計算方式更貼近使用者體驗,可以獲取更多的問題資訊,比如路由跳轉的時長問題等。

02

Flutter的具體實現

Flutter的可互動時長end點採用的演算法與native一致,可見元件滿足頁面填充程度並且完成心跳檢查的情況下任務可互動,另外對於一些比較空的頁面,元件面積小,無法達到水平>60%,垂直>80%的條件,就用互動前最後一次Frame重新整理時間點作為end點。

具體流程如下圖:

走近科學,探究阿里閒魚團隊透過資料提升Flutter體驗的真相


03

效果

由於debug模式採用的JIT編譯,debug模式下體驗載入時長偏長,但是release模式下的AOT編譯時長明顯縮短很多,整體頁面載入時長還是要優於weex。

Exception率

Flutter部分exception/error 會導致程式碼後面的邏輯沒有走到造成頁面或邏輯bug,所以Flutter的exception需要作為穩定性的標準之一。

01

定義

FlutterException率 = exception發生次數 / Flutter頁面PV

分子:exception發生次數(已過濾掉白名單)

分母:Flutter頁面PV

具體實現如下:

  1. Future<Null> main() async {

  2.  FlutterError.onError = (FlutterErrorDetails details) async {

  3.    Zone.current.handleUncaughtError(details.exception, details.stack);

  4.  };


  5.  runZoned<Future<Null>>(() async {

  6.    runApp(new HomeApp());

  7.  }, onError: (error, stackTrace) async {

  8.    await _reportError(error, stackTrace);

  9.  });

  10. }

其中,FlutterError.onError只會捕獲Flutter framework層的error和exception,官方建議將這個方法按照自己的exception捕獲上報需求定製。在實踐過程中,我們遇到很多不會對使用者體驗產生任何影響的exception會被頻繁觸發,這類沒有改善意義的exception可以新增白名單過濾上報。

02

效果

有了線上exception的監控,可以及早發現隱患,獲取問題堆疊資訊,方便定位bug,提示整體穩定性。

總結

到這裡,我們完成Flutter頁面滑動流暢度、頁面載入時長和Exception率的統計,對於Flutter的效能有一個具體的數字化標準,對以後的使用者體驗提升和效能問題排查提供基礎。目前閒魚客戶端的商品詳情頁和主釋出頁已經全量Flutter化,感興趣的同學可以體驗下這兩個頁面和其他頁面的效能差異,最後歡迎大家提供反饋和建議。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900359/viewspace-2642553/,如需轉載,請註明出處,否則將追究法律責任。

相關文章