Flutter 的可視化界面、繪製性能調優 🔧 —— DevTools

前言

給你們介紹DevTools的主要緣由主要有幾點 🚀 :git

  • 首先是DevTools自己是Flutter官方推薦的一個調試工具。
  • DevTools是用Flutter編寫的,極具特點 🖼 。
  • 擁有很是全面的調試功能,能夠知足大小、方面不一樣的優化需求 ⚙️。

DevTools

首先,隆重介紹今天的主角:DevTools 👏web

官網介紹:DevTools is a suite of performance and debugging tools for Dart and Flutter. It’s currently in beta release, but is under active development.編程

翻譯:DevToolsDartFlutter的一套性能和調試工具。它目前處於測試版,但正在積極開發中。後端

安裝 🔌

要使用DevTools首先,全部的一切的前提,確定是,裝上它,yes,這看似簡單的一步其實花了我還蠻久時間的。但願各位安裝順利,我將給各位介紹四種打開DevTools的方法瀏覽器

Android Studio / Intellij

使用以上兩種開發工具的小夥伴看這邊了 🙋‍♂️,在此類開發工具上打開DevTools一共分三個步驟markdown

  1. 安裝 Flutter 插件:

首先確認你已經在開發工具內安裝了Flutter 插件(不會有人還沒裝吧,不會吧 👀 )



app

  1. 開始調試一個應用 🔩

對,就是字面意思,容許你要調試的那個項目工程(推薦使用Profile模式)。友情提示:Profile模式只能在真機 📱 上運行。異步

  1. 找到 DevTools 的入口

其實 DevTools 的入口不是很顯眼,可是還算好找,在底部的工具欄,我們常常用的hot reload邊上,直接點擊該按鈕 🔘 就能夠啓動該項目的DevTools

函數

(PS:我本身用的是Android Studio,可是我並無經過這種方式成功,一直就是Installing DevTools...,若是各位知道緣由,歡迎在評論區指點一下,阿里嘎多 🤗 )工具

VS Code

  1. 安裝 Flutter 插件:

首先確認你已經在開發工具內安裝了FlutterDart 插件(同Android Studio

  1. 經過在 VS Code 中打開你的項目的根目錄(包含 pubspec.yaml)並點擊 Run > Debugging (F5),來開啓調試會話。

  2. 啓動開發程序

  • 一旦調試會話處於活躍且應用程序已開啓 🏃🏻 ,那麼 VS Code 命令控制板中將會顯示 Dart: Open DevTools

  • 當你第一次運行時(以及將來更新開發工具包時),系統會提醒你激活或升級DevTools

Command Line (命令行)

  1. 安裝開發者工具

若是在你的環境變量 PATH 中有 pub, 能夠運行 🚀:

pub global activate devtools
複製代碼

若是環境變量 PATH 中有 flutter , 能夠運行 🚀:

flutter pub global activate devtools
複製代碼
  1. 啓動開發者工具服務

下一步,啓動本地 web server 服務來運行開發者工具。運行下面兩個命令中的一個。

pub global run devtools   # If you have `pub` on your path.
複製代碼
flutter pub global run devtools   # If you have `flutter` on your path.
複製代碼

運行這兩行代碼以後,在命令行應該會有這樣一個輸出:

An Observatory debugger and profiler on iPhone X is available
at: http://127.0.0.1:50976/Swm0bjIe0ks=/
複製代碼

at後面的就是DevTools的地址,你們直接用Chrome打開便可。打開後的網頁須要你填寫一個連接,也就是你須要調試的項目的Debug service監聽地址,這個地址哪兒來的呢 🤔 ?



3. 啓動一個 appdebug

其實上面所需的連接 🔗 地址,就在咱們平時運行項目的Log中。你們運行工程,在日誌的前幾行就能夠找到Debug service的地址,就是圖中 listening on以後的部分,將該連接填入咱們在第二步打開的網頁中,就能夠進入到咱們項目對應的DevTools之中。

暴力打開

以上三種是Flutter官網提供的安裝、打開方式,很惋惜,我都沒有打開成功 🤯 ,不是一直安裝,就是打開了一個很是老的版本的DevTools,應該是和個人Dart SDK路徑配置有關,因此通過個人研究,發現了一個很是痛快 🤫 的打開方法(目前暫不知有什麼弊端)。

  1. 找到Flutter文件夾的地址。

這個因人而異,各位應該清楚本身電腦上flutter的路徑吧,好比我就放在我的的development目錄下:

/Users/tys/development/flutter
複製代碼
  1. 找到DevTools

這就是我這邊要說的暴力打開的關鍵 🔑 ,我發現...,這個DevTools,好像已經悄悄的放在了Flutter中。

/Users/tys/development/flutter/.pub-cache/bin/devtools
複製代碼

在上面這個目錄下,你們能夠直接找到它,雙擊點開,直接好傢伙 💡



蕪湖~直接到達上面用命令行打開的最後一步。並且這就是你當前使用的Flutter版本對應的最新的、可用的DevTools

使用 DevTools🕵🏻‍♂️‘

我將按照DevTools頂部工具欄的順序給各位逐個介紹各個功能 🤓 。

Flutter inspector

  • 簡介:這一部分的功能主要是面向各位的界面模塊,裏面首先會展現你總體的佈局繪製樹 🌲 ,點擊樹的每一個節點都會在右側顯示更細節的佈局信息 🐳 ,同時,在佈局信息上你能夠看到可視化的視圖信息並進行一些簡單的選擇操做,這部分功能用官方的話說主要有兩個目的:

  1. 瞭解現有佈局。
  2. 診斷佈局問題。

  有關這個模塊的內容咱們根據下圖中紅色標註的順序一塊兒看一下 🔍

Select widget mode

  這實際上是一個選擇視圖,你們看到這個Tab下對應的其實就是你的整個繪製樹 🌲 了,這樣一顆樹的結構複雜度通常都會和你的頁面複雜度相關。若是是一個複雜的頁面結構,這棵樹也會很是龐大,看起來會很模糊,爲了讓咱們能夠更好的看到樹裏面的細節結構,咱們就能夠點擊樹上的某個節點,區查看樹的某一部分的詳細內容 🔮 。

Detail Tree

  顧名思義,其實這裏就是咱們剛纔所選擇的節點的詳細信息,其實現實的信息真的能夠說很是走心 ❤️ 了。看圖中,我選了一個Container節點 ,Container下的未賦值屬性都給你提示出來了,如bg背景顏色等。若是你的頁面內有顏色、色彩的疊加之類的,經過它能夠看的很清晰 🔬 。

Layout Explorer

   這個就厲害了,是你當前選中節點的佈局瀏覽器。好比你們能夠看到,我選中的這個Container下面有包裹的詳細結構都有顯示出來,包括各個Wiget的邊界,甚至給你顯示了計算後的高度和寬度 📏 (按照標準的分辨率尺寸)。


  Layout Explorer不只能讓你查看整個佈局界面,還可讓你作一些簡單的動態操做,讓你在不改動代碼的前提下,明確你的佈局問題,或者缺陷(請看動圖⬇️ )。

Slow Animations

  點擊該按鈕,將下降你App內的全部動畫效果的速度,感覺0.5倍速的神奇世 🤩 ,這裏說的Animations包括但不只限於你的界面跳轉,Hero等系統級動畫,我的認爲這部分功能主要有兩個做用

   1. 若是在該模式下你的動畫不存在任何卡頓,則說明你的動畫效能很是完美,能夠達到比較好的用戶體驗 👍 。
   2. 讓部分動畫製做者看清本身的動畫繪製曲線、路徑 🕳 。

Debug Paint

  在渲染中添加視覺調試提示,以顯示邊框,填充,對齊和間隔。會在你的模擬器上顯示總體的佈局狀況,這些狀況包括你的總體渲染方向和基礎組件類。

Paint Baselines

  使每一個RenderBox在其每一個文本基線處繪製一條線。就是下圖中的綠線 ⬇️

Repaint Rainbow

  打開這個功能,你的界面在重繪時,會在重繪的部分更改一個邊界顏色 🎆 。好比,你有一個banner,間隔1s換一張圖片,那麼每一秒你的banner圖周邊的顏色就會變一個隨機顏色。我的感受其目的是讓咱們看到當前頁面正在繪製或重繪的部分。

Timeline 🌟

在進行性能調試時請使用實際設備調試。Skia有兩套很不一樣的後端,Flutter在iOS模擬器中使用純CPU後端,而在實際設備通常使用GPU硬件加速後端,因此性能特性很不同。

TimeLineDevTools中比較實用有實際意義的部分。可讓你實際看到你的APP存在的UI問題或GPU問題。接下來我將帶各位來學習如何去分析本身Flutter APP的繪製性能 🩺 。


  你們首先能夠看到,在上圖中最上層的部分是一個柱形圖 📊 。這部分是你剛纔所作操做的一個繪製狀態圖,根據右邊的圖例,也能明白其中的意思,這邊我再給各位明確一下。

UI、Raster、Jank

  你們看柱狀圖中有紅色有藍色,淡藍色的是UI線程的繪製狀況、深藍色是你的柵格化線程(GPU線程)。紅色,表明你的繪製出現了卡頓。通常來講咱們在Flutter裏咱們定義卡頓:若是一幀的渲染時間超過16ms,則會被認爲此幀是延時的,爲了達到幀渲染頻率到 60 FPS (每秒幀數),每一幀的渲染時間必須等於或少於 16 ms。若是沒有達到這個目標,你會發現 UI 不流暢或丟幀 📌 。

Timeline Event

  咱們在整個界面的中間部分能夠看到的是Timeline Event,它其實對應的就是咱們上面繪製狀況圖的細節狀況,是繪製過程當中的事件。咱們利用它能夠詳細的看見咱們卡頓的緣由。咱們挑選其中卡頓的一幀,來仔細分析一下 🧮 。
  在下圖中你們能夠看到在Timeline Event中,依然分爲了兩個部分,上半部分是UI Event,下半部分是Raster Event。在這一陣中,咱們能夠明顯的感受到的是,咱們的UI Event佔用了很是長的時間 ⏰ 。爲了你們能對整個TimeLine Event的含義有更好的理解,這裏引用了官方的一段解釋:

火焰圖選項卡用於顯示選中幀事件,CPU 的樣本信息。圖表展現的是自上而下的調用堆棧信息,即上面的堆棧幀調用下面的堆棧幀。每個堆棧幀的寬度表明 CPU 執行的時長。棧幀消耗 CPU 的時間越長,就越洽有多是咱們進行性能改進的好地方。

  咱們在上面的堆棧信息中能夠看到最上層的一個EventVsyncProgressCallback,在下圖中,咱們能夠在底部看到,這個堆棧的耗時高達40.6ms,幾乎是沒法接受的🚽 。其實咱們點擊一個流暢的幀後,發現其中也會有VsyncProgressCallback。那麼爲何這一幀會格外的長,並形成卡頓呢?

  所以咱們爲了進一步探究其中的緣由,咱們須要進一步的深刻探究,咱們須要更多的信息。

  • 更詳細的火焰圖 🔥 (CPU Flame Chart

  這裏有你想要的細節信息,包括內部具體某一個堆棧的耗時狀況。爲什麼VsyncProgressCallback耗時如此之久。內部有哪些細節操做,這裏一清二楚 🐮 。這裏咱們能夠看到,一個長耗時VsyncProgressCallback相比一個短耗時的VsyncProgressCallback多了幾個部分。在清楚VsyncProgressCallback的內部堆棧信息後,咱們就能夠有針對性的再對內部的耗時堆棧進行分析。切換到咱們第二個Tab

有關Flutter中的Vsync信號推薦閱讀: Flutter渲染機制 - UI線程 🔗 。

  • 調用樹

  這部分更貼近咱們開發者,咱們能夠詳細看到VsyncProgressCallback具體調用了哪些函數,哪些函數是耗時比較久的。你們再結合上面一張圖,咱們在卡頓這一幀多作的工做,或者說多調用的函數,就是這裏的performRebuild,點開函數的具體調用,咱們能夠發現,這個函數一直在循環調用。沒錯這裏的performRebuild其實就是一個遍歷你繪製樹 🌲 的過程,說明了你在當前幀,更新了一個龐大的繪製樹,而且樹上的多個子節點也進行了從新繪製。因此咱們應該要排查的就是,咱們在界面中的哪一個部分集中調用了多個組件的重繪,或者哪部分的view_mode,驅動了這樣的一個重繪過程,其中是否有沒必要要的重繪。以此來提升渲染性能 😆 。

  上面一部分說明了咱們的UI線程阻塞的部分緣由,和排查方法,那若是是GPU呢?一樣,咱們選取了一幀典型的GPU耗時操做,在這個堆棧信息中咱們能夠看到咱們的GPU方法確實佔用了大量的時間 ⏳ ,一樣是GPURasterizer::Draw爲何某一幀會特別久呢?

  其實在DevToolsRaster視圖中,並不能將這部分展現的很是清晰。可是還有一個辦法能夠深究其緣由。你們能夠直接打開以前填入DevTools的連接。若是你正在profile模式下運行,你的log中就能夠找到這個連接 🔗 。

  點開這個連接,在這個頁面的中間部分,咱們能夠找到timeline工具(我已經在圖中用紅框標出)。

  進入這部分timeline以後,這裏就是最最最最詳細的每一幀的信息了 📌 。你能夠用鼠標選中一塊區域,在下方就會輸出你選中區域的具體函數調用狀況。包括函數名稱、總耗時、調用次數、每次耗時以及一個可視化的柱狀圖 📊 佔比狀況。一目瞭然。有關GPU部分的函數調用須要各位對Flutter Engine的源碼有比較高的理解,纔能有針對性的解決問題。其中相關的調用棧很是的複雜。

  官方聲明的會大量耗時的skia函數調用:

  • saveLayer: 很是耗時,每次調用須要在CPU裏從新分配一塊繪圖緩衝區,而且告訴GPU切換繪圖目標。尤爲在比較老的設備上。
  • clipPath: 耗時,每次調用,會影響接下來每個繪圖指令,當繪圖指令複雜時,會作屢次和clipPath的相交操做,把在clipPath以外的部分剔除。

  可能你們會說,這部分函數,平時在編程的時候幾乎沒有使用 🤯 ,但其實他們普遍存在於咱們常見的Widget及其屬性中。好消息是Flutter Team在過去的更新迭代中已經剔除了很是多的沒必要要的相似上述的耗時操做 🐳 。因此當你們在本身的timeline中找到相似的耗時操做後,仍是能夠經過一些Widget的組件源碼去分析找到出處,相較之前容易得多。

在上圖的函數調用狀況圖中,短耗時的操做不表明其沒有可能形成卡頓。有可能你找到了saveLayer的調用,但沒有想象中的耗時。實際是由於:Skia的GPU的後端在接收到saveLayer的指令後會進行一個簡單的預處理,就當即返回,形成不耗時的假象。但實際後續異步的過程當中還會調用SkCanvas::Flush形成大量耗時。這裏異步處理的目的是將多條預處理消息打包 📦 送至GPU進行繪製。

結束語

   有關Flutter中的界面繪製性能相關就介紹到這裏了,但願這篇文章能給各位起到必定的幫助做用 🤪 ,後續會繼續介紹Flutter相關的內存管理和其餘相關知識,敬請期待 🧸 。
   參考:DevTools

相關文章
相關標籤/搜索