您好,登錄后才能下訂單哦!
這篇文章主要介紹“分析Flutter應(yīng)用性能檢測(cè)與優(yōu)化”,在日常操作中,相信很多人在分析Flutter應(yīng)用性能檢測(cè)與優(yōu)化問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”分析Flutter應(yīng)用性能檢測(cè)與優(yōu)化”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
圖層分析
Flutter運(yùn)行模式
1、Debug
Debug模式可以在真機(jī)和模擬器上同時(shí)運(yùn)行,此模式會(huì)打開(kāi)所有的斷言,包括debugging信息、debugger aids(比如observatory)和服務(wù)擴(kuò)展。優(yōu)化了快速develop/run循環(huán),但是沒(méi)有優(yōu)化執(zhí)行速度、二進(jìn)制大小和部署。命令flutter run就是以這種模式運(yùn)行的,通過(guò)sky/tools/gn --android或者sky/tools/gn --ios來(lái)構(gòu)建應(yīng)用的。
2、Release
Release模式只能在真機(jī)上運(yùn)行,不能在模擬器上運(yùn)行:會(huì)關(guān)閉所有斷言和debugging信息,關(guān)閉所有debugger工具。優(yōu)化了快速啟動(dòng)、快速執(zhí)行和減小包體積。禁用所有的debugging aids和服務(wù)擴(kuò)展。這個(gè)模式是為了部署給最終的用戶(hù)使用。命令flutter run --release就是以這種模式運(yùn)行的,通過(guò)sky/tools/gn --android --runtime-mode=release或者sky/tools/gn --ios --runtime-mode=release來(lái)構(gòu)建應(yīng)用。
3、Profile
Profile模式只能在真機(jī)上運(yùn)行,不能在模擬器上運(yùn)行,基本和Release模式一致,除了啟用了服務(wù)擴(kuò)展和tracing,以及一些為了最低限度支持tracing運(yùn)行的東西(比如可以連接observatory到進(jìn)程)。命令flutter run --profile就是以這種模式運(yùn)行的,通過(guò)sky/tools/gn --android --runtime-mode=profile或者sky/tools/gn --ios --runtime-mode=profile來(lái)構(gòu)建應(yīng)用。
4、test
headless test模式只能在桌面上運(yùn)行,基本和Debug模式一致,除了是headless的而且你能在桌面運(yùn)行。命令flutter test就是以這種模式運(yùn)行的,通過(guò)sky/tools/gn來(lái)build。
在實(shí)際開(kāi)發(fā)中,應(yīng)該用到上面所說(shuō)的四種模式又各自分為兩種:一種是未優(yōu)化的模式,供開(kāi)發(fā)人員調(diào)試使用;一種是優(yōu)化過(guò)的模式,供最終的開(kāi)發(fā)人員使用。默認(rèn)情況下是未優(yōu)化模式,如果要開(kāi)啟優(yōu)化模式,build的時(shí)候在命令行后面添加--unoptimized參數(shù)。
不管是移動(dòng)開(kāi)發(fā)還是前端開(kāi)發(fā),對(duì)于性能問(wèn)題分析的思路都是先分析并定位問(wèn)題,F(xiàn)lutter也不例外,借助Flutter 提供的度量性能工具,我們可以快速定位代碼中的性能問(wèn)題,而性能圖層就是幫助我們確認(rèn)問(wèn)題影響范圍的利器,它類(lèi)似Android的圖層分析工具。
為了使用性能圖層,F(xiàn)lutter提供了分析(Profile)模式,與調(diào)試代碼可以通過(guò)模擬器在調(diào)試模式下找到代碼邏輯 Bug 不同,性能問(wèn)題需要在發(fā)布模式下使用真機(jī)進(jìn)行檢測(cè)。相比發(fā)布(Release)模式而言,調(diào)試模式增加了很多額外的檢查(比如斷言),這些檢查可能會(huì)耗費(fèi)很多資源;更重要的是,調(diào)試模式使用 JIT (即時(shí)編譯)模式運(yùn)行應(yīng)用,代碼執(zhí)行效率較低。這就使得調(diào)試模式運(yùn)行的應(yīng)用,無(wú)法真實(shí)反映出它的性能問(wèn)題。
而另一方面,模擬器使用的指令集為 x86,而真機(jī)使用的指令集是 ARM,由于這兩種方式的二進(jìn)制代碼執(zhí)行行為完全不同,因此模擬器與真機(jī)的性能差異較大。一些 x86 指令集擅長(zhǎng)的操作模擬器會(huì)比真機(jī)快,而另一些操作則會(huì)比真機(jī)慢,這也使得我們無(wú)法使用模擬器來(lái)評(píng)估真機(jī)才能出現(xiàn)的性能問(wèn)題。
為了調(diào)試性能問(wèn)題,我們需要在發(fā)布模式的基礎(chǔ)之上,為分析工具提供少量必要的應(yīng)用追蹤信息,這就是分析模式。除了一些調(diào)試性能問(wèn)題必須的追蹤方法之外,F(xiàn)lutter 應(yīng)用的分析模式和發(fā)布模式的編譯和運(yùn)行是類(lèi)似的,只是啟動(dòng)參數(shù)變成了 profile 而已。我們可以在 Android Studio 中通過(guò)菜單欄點(diǎn)擊 【Run】-【Profile 】‘main.dart’ 選項(xiàng)啟動(dòng)應(yīng)用,也可以通過(guò)命令行參數(shù) flutter run --profile 運(yùn)行 Flutter 應(yīng)用。
渲染問(wèn)題分析
在完成了應(yīng)用啟動(dòng)之后,接下來(lái)我們就可以利用 Flutter 提供的渲染問(wèn)題分析工具,即性能圖層(Performance Overlay)來(lái)分析渲染問(wèn)題了。性能圖層會(huì)在當(dāng)前應(yīng)用的最上層,以 Flutter 引擎自繪的方式展示 GPU 與 UI 線程的執(zhí)行圖表,而其中每一張圖表都代表當(dāng)前線程最近 300 幀的表現(xiàn),如果 UI 產(chǎn)生了卡頓(跳幀),這些圖表可以幫助我們分析并找到原因,如下圖所示。
上圖演示了性能圖層的展現(xiàn)樣式。其中,GPU 線程的性能情況在上面,UI 線程的情況顯示在下面,藍(lán)色垂直的線條表示已執(zhí)行的正常幀,綠色的線條代表的是當(dāng)前幀。
同時(shí),為了保持 60Hz 的刷新頻率,GPU 線程與 UI 線程中執(zhí)行每一幀耗費(fèi)的時(shí)間都應(yīng)該小于 16ms(1/60 秒)。在這其中有一幀處理時(shí)間過(guò)長(zhǎng),就會(huì)導(dǎo)致界面卡頓,圖表中就會(huì)展示出一個(gè)紅色豎條,如下圖所示。
如果紅色豎條出現(xiàn)在 GPU 線程圖表,意味著渲染的圖形太復(fù)雜,導(dǎo)致無(wú)法快速渲染;而如果是出現(xiàn)在了 UI 線程圖表,則表示 Dart 代碼消耗了大量資源,需要優(yōu)化代碼執(zhí)行時(shí)間。
GPU問(wèn)題定位
GPU渲染問(wèn)題主要集中在底層渲染耗時(shí)上,有時(shí)候 Widget 樹(shù)雖然構(gòu)造起來(lái)容易,但在 GPU 線程下的渲染卻很耗時(shí)。例如,涉及 Widget 裁剪、蒙層這類(lèi)多視圖疊加渲染,或是由于缺少緩存導(dǎo)致靜態(tài)圖像的反復(fù)繪制,都會(huì)明顯拖慢 GPU 的渲染速度。
接下來(lái),使用性能圖層提供的兩項(xiàng)參數(shù),即檢查多視圖疊加的視圖渲染開(kāi)關(guān) checkerboardOffscreenLayers和檢查緩存的圖像開(kāi)關(guān)checkerboardRasterCacheImages來(lái)檢查這兩種情況。
checkerboardOffscreenLayers
多視圖疊加通常會(huì)用到 Canvas 里的 savaLayer 方法,這個(gè)方法在實(shí)現(xiàn)一些特定的效果(比如半透明)時(shí)非常有用,但由于其底層實(shí)現(xiàn)會(huì)在 GPU 渲染上涉及多圖層的反復(fù)繪制,因此會(huì)帶來(lái)較大的性能問(wèn)題。
對(duì)于 saveLayer 方法使用情況的檢查,我們只需要在 MaterialApp 的初始化方法中,將 checkerboardOffscreenLayers 開(kāi)關(guān)設(shè)置為 true,分析工具就會(huì)自動(dòng)幫我們檢測(cè)多視圖疊加的情況。使用了 saveLayer 的 Widget 會(huì)自動(dòng)顯示為棋盤(pán)格式,并隨著頁(yè)面刷新而閃爍。不過(guò),saveLayer 是一個(gè)較為底層的繪制方法,因此我們一般不會(huì)直接使用它,而是會(huì)通過(guò)一些功能性 Widget,在涉及需要剪切或半透明蒙層的場(chǎng)景中間接地使用。所以一旦遇到這種情況,我們需要思考一下是否一定要這么做,能不能通過(guò)其他方式來(lái)實(shí)現(xiàn)呢?
比如下面的例子中,我們使用 CupertinoPageScaffold 與 CupertinoNavigationBar 實(shí)現(xiàn)了一個(gè)動(dòng)態(tài)模糊的效果,代碼如下:
CupertinoPageScaffold( navigationBar: CupertinoNavigationBar(),//動(dòng)態(tài)模糊導(dǎo)航欄 child: ListView.builder( itemCount: 100, //為列表創(chuàng)建100個(gè)不同顏色的RowItem itemBuilder: (context, index)=>TabRowItem( index: index, lastItem: index == 100 - 1, color: colorItems[index],//設(shè)置不同的顏色 colorName: colorNameItems[index], ) ) );
其中,模糊的NavigationBar效果如下圖所示。
當(dāng)我們開(kāi)啟checkerboardOffscreenLayers之后,可以看到視圖蒙層效果對(duì)GPU的渲染壓力導(dǎo)致性能視圖頻繁閃動(dòng)。如果我們沒(méi)有對(duì)動(dòng)態(tài)模糊效果有特殊需求,則可以使用不帶模糊效果的 Scaffold 和白色的 AppBar 實(shí)現(xiàn)同樣的產(chǎn)品功能,來(lái)解決這個(gè)性能問(wèn)題。
Scaffold( //使用普通的白色AppBar appBar: AppBar(title: Text('Home', style: TextStyle(color:Colors.black),),backgroundColor: Colors.white), body: ListView.builder( itemCount: 100, //為列表創(chuàng)建100個(gè)不同顏色的RowItem itemBuilder: (context, index)=>TabRowItem( index: index, lastItem: index == 100 - 1, color: colorItems[index],//設(shè)置不同的顏色 colorName: colorNameItems[index], ) ), );
運(yùn)行一下代碼,可以看到,在去掉了模糊效果之后,GPU 的渲染壓力得到了緩解,checkerboardOffscreenLayers 檢測(cè)圖層也不再頻繁閃爍了。
checkerboardRasterCacheImages
從資源的角度看,另一類(lèi)非常消耗性能的操作是渲染圖像,因?yàn)閳D像渲染會(huì)涉及 I/O、GPU 存儲(chǔ)以及不同通道的數(shù)據(jù)格式轉(zhuǎn)換,因此渲染過(guò)程的構(gòu)建需要消耗大量資源。為了緩解 GPU 的壓力,F(xiàn)lutter 提供了多層次的緩存快照,這樣 Widget 重建時(shí)就無(wú)需重新繪制靜態(tài)圖像了。
與檢查多視圖疊加渲染的 checkerboardOffscreenLayers 參數(shù)類(lèi)似,F(xiàn)lutter 提供了檢查緩存圖像的開(kāi)關(guān) checkerboardRasterCacheImages,來(lái)檢測(cè)在界面重繪時(shí)頻繁閃爍的圖像。
為了提高靜態(tài)圖像顯示性能,我們可以把需要靜態(tài)緩存的圖像加到 RepaintBoundary 中,RepaintBoundary 可以確定 Widget 樹(shù)的重繪邊界,如果圖像足夠復(fù)雜,F(xiàn)lutter 引擎會(huì)自動(dòng)將其緩存,從而避免重復(fù)刷新。當(dāng)然,因?yàn)榫彺尜Y源有限,如果引擎認(rèn)為圖像不夠復(fù)雜,也可能會(huì)忽略 RepaintBoundary。下面的代碼展示了通過(guò) RepaintBoundary,將一個(gè)靜態(tài)復(fù)合 Widget 加入緩存的具體用法,如下所示。
RepaintBoundary(//設(shè)置靜態(tài)緩存圖像 child: Center( child: Container( color: Colors.black, height: 10.0, width: 10.0, ), ));
UI 線程問(wèn)題定位
如果說(shuō) GPU 線程問(wèn)題定位的是渲染引擎底層渲染異常,那么 UI 線程問(wèn)題發(fā)現(xiàn)的則是應(yīng)用的性能瓶頸。比如在視圖構(gòu)建時(shí),在 build 方法中使用了一些復(fù)雜的運(yùn)算,或是在主 Isolate 中進(jìn)行了同步的 I/O 操作。這些問(wèn)題,都會(huì)明顯增加 CPU 的處理時(shí)間,拖慢應(yīng)用的響應(yīng)速度。
針對(duì)這類(lèi)問(wèn)題,我們可以使用 Flutter 提供的 Performance 工具,來(lái)記錄應(yīng)用的執(zhí)行軌跡。Performance 是一個(gè)強(qiáng)大的性能分析工具,能夠以時(shí)間軸的方式展示 CPU 的調(diào)用棧和執(zhí)行時(shí)間,去檢查代碼中可疑的方法調(diào)用。
打開(kāi) Android Studio 底部工具欄中的“Open DevTools”按鈕之后,系統(tǒng)會(huì)自動(dòng)打開(kāi) Dart DevTools 的網(wǎng)頁(yè),將頂部的 tab 切換到 Performance 后,我們就可以開(kāi)始分析代碼中的性能問(wèn)題了。
接下來(lái),我們通過(guò)一個(gè)在 ListView 中計(jì)算 MD5 的例子來(lái)演示 Performance 的具體分析過(guò)程??紤]到在 build 函數(shù)中進(jìn)行渲染信息的組裝是一個(gè)常見(jiàn)的操作,為了演示Performance的使用過(guò)程,我們故意放大計(jì)算 MD5 的耗時(shí),如循環(huán)迭代計(jì)算了 1 萬(wàn)次。
class MyHomePage extends StatelessWidget { MyHomePage({Key key}) : super(key: key); String generateMd5(String data) { //MD5固定算法 var content = new Utf8Encoder().convert(data); var digest = md5.convert(content); return hex.encode(digest.bytes); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('demo')), body: ListView.builder( itemCount: 30,// 列表元素個(gè)數(shù) itemBuilder: (context, index) { //反復(fù)迭代計(jì)算MD5 String str = '1234567890abcdefghijklmnopqrstuvwxyz'; for(int i = 0;i<10000;i++) { str = generateMd5(str); } return ListTile(title: Text("Index : $index"), subtitle: Text(str)); }// 列表項(xiàng)創(chuàng)建方法 ), ); } }
與性能圖層能夠自動(dòng)記錄應(yīng)用執(zhí)行情況不同,使用 Performance 來(lái)分析代碼執(zhí)行軌跡,我們需要手動(dòng)點(diǎn)擊【Record】按鈕去主動(dòng)觸發(fā),在完成信息的抽樣采集后再點(diǎn)擊【Stop】按鈕結(jié)束錄制,然后就可以得到在這期間應(yīng)用的執(zhí)行情況了。
Performance 記錄的應(yīng)用執(zhí)行情況叫做 CPU 幀圖,又被稱(chēng)為火焰圖。火焰圖是基于記錄代碼執(zhí)行結(jié)果所產(chǎn)生的圖片,用來(lái)展示 CPU 的調(diào)用棧,表示的是 CPU 的繁忙程度。所以,我們要檢測(cè) CPU 耗時(shí)問(wèn)題,皆可以查看火焰圖底部的哪個(gè)函數(shù)占據(jù)的寬度最大。只要有“平頂”,就表示該函數(shù)可能存在性能問(wèn)題,如下圖所示。
可以看到,_MyHomePage.generateMd5 函數(shù)的執(zhí)行時(shí)間最長(zhǎng),幾乎占滿(mǎn)了整個(gè)火焰圖的寬,而這也與代碼中存在的問(wèn)題是一致的。在找到了問(wèn)題之后,我們就可以使用 Isolate(或 compute)將這些耗時(shí)的操作挪到并發(fā)主 Isolate 之外去完成了。
到此,關(guān)于“分析Flutter應(yīng)用性能檢測(cè)與優(yōu)化”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。