偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Flutter應(yīng)用性能檢測與優(yōu)化

開發(fā) 前端
對于應(yīng)用渲染并不流暢,出現(xiàn)了性能問題,我們該如何檢測,又該從哪里著手處理呢?和移動開發(fā)類似, Flutter 的性能問題主要可以分為 GPU 線程問題和 UI 線程(CPU)問題兩類。

概述

軟件項目的交付是一個復(fù)雜且漫長的過程,任何細小的失誤都有可能導致交付過程失敗。在軟件開發(fā)過程中,除了代碼邏輯的 Bug 和視覺異常這些功能層面的問題之外,移動應(yīng)用另一類常見的問題是性能問題,比如滑動操作不流暢、頁面出現(xiàn)卡頓丟幀現(xiàn)象等。這些問題雖然不至于讓移動應(yīng)用完全不可用,但也很容易引起用戶反感,從而對應(yīng)用質(zhì)量產(chǎn)生質(zhì)疑,甚至失去耐心。

那么,對于應(yīng)用渲染并不流暢,出現(xiàn)了性能問題,我們該如何檢測,又該從哪里著手處理呢?和移動開發(fā)類似, Flutter 的性能問題主要可以分為 GPU 線程問題和 UI 線程(CPU)問題兩類。對于這些問題,有一個通用的套路:首先,都需要先通過性能圖層進行初步分析,而一旦確認問題存在,接下來就是利用 Flutter 提供的各類分析工具來進行問題定位。

圖層分析

Flutter運行模式

1、Debug

Debug模式可以在真機和模擬器上同時運行,此模式會打開所有的斷言,包括debugging信息、debugger aids(比如observatory)和服務(wù)擴展。優(yōu)化了快速develop/run循環(huán),但是沒有優(yōu)化執(zhí)行速度、二進制大小和部署。命令flutter run就是以這種模式運行的,通過sky/tools/gn --android或者sky/tools/gn --ios來構(gòu)建應(yīng)用的。

2、Release

Release模式只能在真機上運行,不能在模擬器上運行:會關(guān)閉所有斷言和debugging信息,關(guān)閉所有debugger工具。優(yōu)化了快速啟動、快速執(zhí)行和減小包體積。禁用所有的debugging aids和服務(wù)擴展。這個模式是為了部署給最終的用戶使用。命令flutter run --release就是以這種模式運行的,通過sky/tools/gn --android --runtime-mode=release或者sky/tools/gn --ios --runtime-mode=release來構(gòu)建應(yīng)用。

3、Profile

Profile模式只能在真機上運行,不能在模擬器上運行,基本和Release模式一致,除了啟用了服務(wù)擴展和tracing,以及一些為了最低限度支持tracing運行的東西(比如可以連接observatory到進程)。命令flutter run --profile就是以這種模式運行的,通過sky/tools/gn --android --runtime-mode=profile或者sky/tools/gn --ios --runtime-mode=profile來構(gòu)建應(yīng)用。

4、test

headless test模式只能在桌面上運行,基本和Debug模式一致,除了是headless的而且你能在桌面運行。命令flutter test就是以這種模式運行的,通過sky/tools/gn來build。

在實際開發(fā)中,應(yīng)該用到上面所說的四種模式又各自分為兩種:一種是未優(yōu)化的模式,供開發(fā)人員調(diào)試使用;一種是優(yōu)化過的模式,供最終的開發(fā)人員使用。默認情況下是未優(yōu)化模式,如果要開啟優(yōu)化模式,build的時候在命令行后面添加--unoptimized參數(shù)。

不管是移動開發(fā)還是前端開發(fā),對于性能問題分析的思路都是先分析并定位問題,F(xiàn)lutter也不例外,借助Flutter 提供的度量性能工具,我們可以快速定位代碼中的性能問題,而性能圖層就是幫助我們確認問題影響范圍的利器,它類似Android的圖層分析工具。

為了使用性能圖層,F(xiàn)lutter提供了分析(Profile)模式,與調(diào)試代碼可以通過模擬器在調(diào)試模式下找到代碼邏輯 Bug 不同,性能問題需要在發(fā)布模式下使用真機進行檢測。相比發(fā)布(Release)模式而言,調(diào)試模式增加了很多額外的檢查(比如斷言),這些檢查可能會耗費很多資源;更重要的是,調(diào)試模式使用 JIT (即時編譯)模式運行應(yīng)用,代碼執(zhí)行效率較低。這就使得調(diào)試模式運行的應(yīng)用,無法真實反映出它的性能問題。

而另一方面,模擬器使用的指令集為 x86,而真機使用的指令集是 ARM,由于這兩種方式的二進制代碼執(zhí)行行為完全不同,因此模擬器與真機的性能差異較大。一些 x86 指令集擅長的操作模擬器會比真機快,而另一些操作則會比真機慢,這也使得我們無法使用模擬器來評估真機才能出現(xiàn)的性能問題。

為了調(diào)試性能問題,我們需要在發(fā)布模式的基礎(chǔ)之上,為分析工具提供少量必要的應(yīng)用追蹤信息,這就是分析模式。除了一些調(diào)試性能問題必須的追蹤方法之外,F(xiàn)lutter 應(yīng)用的分析模式和發(fā)布模式的編譯和運行是類似的,只是啟動參數(shù)變成了 profile 而已。我們可以在 Android Studio 中通過菜單欄點擊 【Run】-【Profile 】‘main.dart’ 選項啟動應(yīng)用,也可以通過命令行參數(shù) flutter run --profile 運行 Flutter 應(yīng)用。

渲染問題分析

在完成了應(yīng)用啟動之后,接下來我們就可以利用 Flutter 提供的渲染問題分析工具,即性能圖層(Performance Overlay)來分析渲染問題了。性能圖層會在當前應(yīng)用的最上層,以 Flutter 引擎自繪的方式展示 GPU 與 UI 線程的執(zhí)行圖表,而其中每一張圖表都代表當前線程最近 300 幀的表現(xiàn),如果 UI 產(chǎn)生了卡頓(跳幀),這些圖表可以幫助我們分析并找到原因,如下圖所示。

上圖演示了性能圖層的展現(xiàn)樣式。其中,GPU 線程的性能情況在上面,UI 線程的情況顯示在下面,藍色垂直的線條表示已執(zhí)行的正常幀,綠色的線條代表的是當前幀。

同時,為了保持 60Hz 的刷新頻率,GPU 線程與 UI 線程中執(zhí)行每一幀耗費的時間都應(yīng)該小于 16ms(1/60 秒)。在這其中有一幀處理時間過長,就會導致界面卡頓,圖表中就會展示出一個紅色豎條,如下圖所示。 

如果紅色豎條出現(xiàn)在 GPU 線程圖表,意味著渲染的圖形太復(fù)雜,導致無法快速渲染;而如果是出現(xiàn)在了 UI 線程圖表,則表示 Dart 代碼消耗了大量資源,需要優(yōu)化代碼執(zhí)行時間。

GPU問題定位

GPU渲染問題主要集中在底層渲染耗時上,有時候 Widget 樹雖然構(gòu)造起來容易,但在 GPU 線程下的渲染卻很耗時。例如,涉及 Widget 裁剪、蒙層這類多視圖疊加渲染,或是由于缺少緩存導致靜態(tài)圖像的反復(fù)繪制,都會明顯拖慢 GPU 的渲染速度。

接下來,使用性能圖層提供的兩項參數(shù),即檢查多視圖疊加的視圖渲染開關(guān) checkerboardOffscreenLayers和檢查緩存的圖像開關(guān)checkerboardRasterCacheImages來檢查這兩種情況。

checkerboardOffscreenLayers

多視圖疊加通常會用到 Canvas 里的 savaLayer 方法,這個方法在實現(xiàn)一些特定的效果(比如半透明)時非常有用,但由于其底層實現(xiàn)會在 GPU 渲染上涉及多圖層的反復(fù)繪制,因此會帶來較大的性能問題。

對于 saveLayer 方法使用情況的檢查,我們只需要在 MaterialApp 的初始化方法中,將 checkerboardOffscreenLayers 開關(guān)設(shè)置為 true,分析工具就會自動幫我們檢測多視圖疊加的情況。使用了 saveLayer 的 Widget 會自動顯示為棋盤格式,并隨著頁面刷新而閃爍。不過,saveLayer 是一個較為底層的繪制方法,因此我們一般不會直接使用它,而是會通過一些功能性 Widget,在涉及需要剪切或半透明蒙層的場景中間接地使用。所以一旦遇到這種情況,我們需要思考一下是否一定要這么做,能不能通過其他方式來實現(xiàn)呢?

比如下面的例子中,我們使用 CupertinoPageScaffold 與 CupertinoNavigationBar 實現(xiàn)了一個動態(tài)模糊的效果,代碼如下: 

  1. CupertinoPageScaffold(  
  2.   navigationBar: CupertinoNavigationBar(),//動態(tài)模糊導航欄  
  3.     child: ListView.builder(  
  4.       itemCount: 100,  
  5.       //為列表創(chuàng)建100個不同顏色的RowItem  
  6.       itemBuilder: (context, index)=>TabRowItem(  
  7.             index: index,  
  8.             lastItem: index == 100 - 1,  
  9.             color: colorItems[index],//設(shè)置不同的顏色  
  10.             colorName: colorNameItems[index],  
  11.           )  
  12.     )  
  13. ); 

其中,模糊的NavigationBar效果如下圖所示。

當我們開啟checkerboardOffscreenLayers之后,可以看到視圖蒙層效果對GPU的渲染壓力導致性能視圖頻繁閃動。如果我們沒有對動態(tài)模糊效果有特殊需求,則可以使用不帶模糊效果的 Scaffold 和白色的 AppBar 實現(xiàn)同樣的產(chǎn)品功能,來解決這個性能問題。 

  1. Scaffold(  
  2.   //使用普通的白色AppBar  
  3.   appBar: AppBar(title: Text('Home', style: TextStyle(color:Colors.black),),backgroundColor: Colors.white),  
  4.   body: ListView.builder(  
  5.       itemCount: 100,  
  6.       //為列表創(chuàng)建100個不同顏色的RowItem  
  7.       itemBuilder: (context, index)=>TabRowItem(  
  8.         index: index,  
  9.         lastItem: index == 100 - 1,  
  10.         color: colorItems[index],//設(shè)置不同的顏色  
  11.         colorName: colorNameItems[index],  
  12.       )  
  13.   ),  
  14. ); 

運行一下代碼,可以看到,在去掉了模糊效果之后,GPU 的渲染壓力得到了緩解,checkerboardOffscreenLayers 檢測圖層也不再頻繁閃爍了。

checkerboardRasterCacheImages

從資源的角度看,另一類非常消耗性能的操作是渲染圖像,因為圖像渲染會涉及 I/O、GPU 存儲以及不同通道的數(shù)據(jù)格式轉(zhuǎn)換,因此渲染過程的構(gòu)建需要消耗大量資源。為了緩解 GPU 的壓力,F(xiàn)lutter 提供了多層次的緩存快照,這樣 Widget 重建時就無需重新繪制靜態(tài)圖像了。

與檢查多視圖疊加渲染的 checkerboardOffscreenLayers 參數(shù)類似,F(xiàn)lutter 提供了檢查緩存圖像的開關(guān) checkerboardRasterCacheImages,來檢測在界面重繪時頻繁閃爍的圖像。

為了提高靜態(tài)圖像顯示性能,我們可以把需要靜態(tài)緩存的圖像加到 RepaintBoundary 中,RepaintBoundary 可以確定 Widget 樹的重繪邊界,如果圖像足夠復(fù)雜,F(xiàn)lutter 引擎會自動將其緩存,從而避免重復(fù)刷新。當然,因為緩存資源有限,如果引擎認為圖像不夠復(fù)雜,也可能會忽略 RepaintBoundary。下面的代碼展示了通過 RepaintBoundary,將一個靜態(tài)復(fù)合 Widget 加入緩存的具體用法,如下所示。 

  1. RepaintBoundary(//設(shè)置靜態(tài)緩存圖像  
  2.   child: Center(  
  3.     child: Container(  
  4.       color: Colors.black,  
  5.       height: 10.0,  
  6.       width: 10.0,  
  7.     ),  
  8. )); 

UI 線程問題定位

如果說 GPU 線程問題定位的是渲染引擎底層渲染異常,那么 UI 線程問題發(fā)現(xiàn)的則是應(yīng)用的性能瓶頸。比如在視圖構(gòu)建時,在 build 方法中使用了一些復(fù)雜的運算,或是在主 Isolate 中進行了同步的 I/O 操作。這些問題,都會明顯增加 CPU 的處理時間,拖慢應(yīng)用的響應(yīng)速度。

針對這類問題,我們可以使用 Flutter 提供的 Performance 工具,來記錄應(yīng)用的執(zhí)行軌跡。Performance 是一個強大的性能分析工具,能夠以時間軸的方式展示 CPU 的調(diào)用棧和執(zhí)行時間,去檢查代碼中可疑的方法調(diào)用。

打開 Android Studio 底部工具欄中的“Open DevTools”按鈕之后,系統(tǒng)會自動打開 Dart DevTools 的網(wǎng)頁,將頂部的 tab 切換到 Performance 后,我們就可以開始分析代碼中的性能問題了。

接下來,我們通過一個在 ListView 中計算 MD5 的例子來演示 Performance 的具體分析過程??紤]到在 build 函數(shù)中進行渲染信息的組裝是一個常見的操作,為了演示Performance的使用過程,我們故意放大計算 MD5 的耗時,如循環(huán)迭代計算了 1 萬次。 

  1. class MyHomePage extends StatelessWidget {  
  2.   MyHomePage({Key key}) : super(key: key);  
  3.   String generateMd5(String data) {  
  4.     //MD5固定算法  
  5.     var content = new Utf8Encoder().convert(data);  
  6.     var digest = md5.convert(content);  
  7.     return hex.encode(digest.bytes);  
  8.   }  
  9.   @override  
  10.   Widget build(BuildContext context) {  
  11.     return Scaffold(  
  12.       appBar: AppBar(title: Text('demo')),  
  13.       body: ListView.builder(  
  14.           itemCount: 30,// 列表元素個數(shù)  
  15.           itemBuilder: (context, index) {  
  16.             //反復(fù)迭代計算MD5  
  17.             String str = '1234567890abcdefghijklmnopqrstuvwxyz' 
  18.             for(int i = 0;i<10000;i++) {  
  19.               str = generateMd5(str);  
  20.             }  
  21.             return ListTile(title: Text("Index : $index"), subtitle: Text(str));  
  22.           }// 列表項創(chuàng)建方法  
  23.       ),  
  24.     );  
  25.   }  

與性能圖層能夠自動記錄應(yīng)用執(zhí)行情況不同,使用 Performance 來分析代碼執(zhí)行軌跡,我們需要手動點擊【Record】按鈕去主動觸發(fā),在完成信息的抽樣采集后再點擊【Stop】按鈕結(jié)束錄制,然后就可以得到在這期間應(yīng)用的執(zhí)行情況了。

Performance 記錄的應(yīng)用執(zhí)行情況叫做 CPU 幀圖,又被稱為火焰圖?;鹧鎴D是基于記錄代碼執(zhí)行結(jié)果所產(chǎn)生的圖片,用來展示 CPU 的調(diào)用棧,表示的是 CPU 的繁忙程度。所以,我們要檢測 CPU 耗時問題,皆可以查看火焰圖底部的哪個函數(shù)占據(jù)的寬度最大。只要有“平頂”,就表示該函數(shù)可能存在性能問題,如下圖所示。

可以看到,_MyHomePage.generateMd5 函數(shù)的執(zhí)行時間最長,幾乎占滿了整個火焰圖的寬,而這也與代碼中存在的問題是一致的。在找到了問題之后,我們就可以使用 Isolate(或 compute)將這些耗時的操作挪到并發(fā)主 Isolate 之外去完成了。

總結(jié)

在 Flutter 中,性能分析過程可以分為 GPU 線程問題定位和 UI 線程(CPU)問題定位,而它們都需要在真機上以分析模式(Profile)啟動應(yīng)用,并通過性能圖層分析大致的渲染問題范圍。

一旦確認問題存在,接下來就需要利用 Flutter 所提供的分析工具來定位問題原因了。關(guān)于 GPU 線程渲染問題,我們可以重點檢查應(yīng)用中是否存在多視圖疊加渲染,或是靜態(tài)圖像反復(fù)刷新的現(xiàn)象。而 UI 線程渲染問題,我們則是通過 Performance 工具記錄的火焰圖(CPU 幀圖),分析代碼耗時來找出應(yīng)用執(zhí)行瓶頸。

總的來說,由于 Flutter 采用基于聲明式的 UI 設(shè)計理念,以數(shù)據(jù)驅(qū)動渲染,并采用 Widget->Element->RenderObject 三層結(jié)構(gòu),屏蔽了無謂的界面刷新,能夠保證絕大多數(shù)情況下我們構(gòu)建的應(yīng)用都是高性能的,所以在使用分析工具檢測出性能問題之后,通常我們并不需要做太多的細節(jié)優(yōu)化工作,只需要在改造過程中避開一些常見的坑,就可以獲得優(yōu)異的性能。同時,為了避免造成性能問題,還應(yīng)該從以下幾個方面著手:

  •  控制 build 方法耗時,將 Widget 拆小,避免直接返回一個巨大的 Widget,這樣 Widget 會享有更細粒度的重建和復(fù)用;
  •  盡量不要為 Widget 設(shè)置半透明效果,而是考慮用圖片的形式代替,這樣被遮擋的 Widget 部分區(qū)域就不需要繪制了;
  •  對列表采用懶加載而不是直接一次性創(chuàng)建所有的子 Widget,這樣視圖的初始化時間就減少了。 

 

責任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2012-10-09 09:43:50

WLAN優(yōu)化無線局域網(wǎng)WLAN

2024-03-04 08:00:00

Java開發(fā)

2014-02-20 13:36:35

業(yè)務(wù)服務(wù)管理 應(yīng)用性能優(yōu)化

2011-10-17 09:47:53

應(yīng)用性能工作負載服務(wù)器

2020-07-15 07:00:00

移動應(yīng)用開發(fā)者指南

2010-02-23 16:17:59

2023-07-21 08:42:23

App汽車之家

2023-08-23 08:23:47

應(yīng)用性能優(yōu)化

2025-03-12 04:25:00

Linux系統(tǒng)優(yōu)化應(yīng)用

2021-08-09 16:39:52

工具JVM剖析

2014-08-04 16:38:37

移動應(yīng)用

2023-07-19 15:45:47

ReactDOM輕量級

2015-07-24 16:12:58

應(yīng)用性能管理

2015-12-11 14:02:02

php應(yīng)用

2023-11-06 08:01:09

Go同步異步

2023-08-24 16:54:05

2015-04-03 17:35:50

移動應(yīng)用性能聽云

2014-08-08 15:36:39

Apdex

2012-08-16 13:24:58

Windows 8Windows 7對比

2015-07-29 15:06:21

點贊
收藏

51CTO技術(shù)棧公眾號