iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Mobile Development

通徹 Flutter 學習路徑系列 第 29

通徹 Flutter 學習路徑 Day 29 - 來聊聊 Performance 議題吧

  • 分享至 

  • xImage
  •  

今天的內容會著重在 Flutter 的效能議題
其中會分成四個類型

  1. Speed
  2. Memory
  3. Size
  4. Energy
    這四種類型發生時皆會對使用者體驗造成不好的狀況
    因此讓我們來了解這些問題的發生原因及如何解決吧!
    本篇內容會著重在 Speed 議題上

Speed

Github Issues

在 APP 繪製動畫時,最常被提及的主題便是如何測量效能。
有著 Skia 引擎高速繪製及丟棄 Widgets 的特性,在絕大多數狀況下 Flutter 皆是具備足夠繪製效能的
因此只要避開常見的陷阱便能達到很好的效能

當見到不順暢的動畫出現時,我們必須確保是否有使用 Debug 模式
由於在 Debug 模式下不會使用 Release 的模式執行
在效能上還不到生產環境下的速度
因此在確定效能瓶頸前先檢查是否用的是 Debug 模式或是 Profile 模式!

而以下要提及的是常見的效能瓶頸的狀況

  1. 在每一幀數下重新建置太多 UI Widget,因此可以透過觀察 wigets 重建狀況來查明問題
  2. 建立大量的子節點,而非透過 ListView

而以下則是通用的心法及技巧讓我們可以不寫出效率差的程式碼

Minimize expensive operations

某些操作會相較其他的操作更加耗費時間
因此選擇在適當的狀況下選擇好的操作
可以有效提高運算效能


控制 build() 的開銷

在設計 UI 時,有些東西必須記於心中!

  1. 避免反覆及撰寫花長時間的任務在 build() 函式中,由於 build() 會在上層 Widget
    觸及變化時也一併執行
  2. 避免在 build() 函式中建構大型的單一 Widget,當這樣的狀況發生時,便應該思考將其拆
    分成各自小規模的 Widget ,當然也要額外考慮 Widgets 變化時所帶來的影響
    如:
    setState() 被呼叫時,底下的 Widget 也需要跟著重建,
    因此將具備該函式的 Widget 局部化使得受影響範圍縮小的話,效能也會帶來提升
  3. 避免在 Widget 樹的高層去呼叫 setState() 只為了小部分的 Widget 樹變化

而 Flutter 在遍歷 Widget 樹時,如果該 Widget 與先前繪製的 Widget 相同的情況下
則不會進行重繪,也因此可以優化效能。
而如果可以的話,盡量使用 const constructors 在 Widget 上
它能夠使得 Flutter 停止大多重繪製的作業
而在共享的 UI 上盡量使用 StatelessWidget 來取代函式建置的方式
詳細可參閱

Performance considerations
Widgets vs helper methods


深思熟慮再使用 saveLayer

在某些 Flutter 程式碼會在實作 UI 視覺效果時用到 saveLayer()
然而這個操作是非常花費時間的!
而就算我們本身撰寫的程式碼沒用到,但仍有可能在使用到的 Widget 及套件中使用到該函式
如果在沒有控管好的狀況下胡亂使用的話,就可能會導致 jank 的發生
!jank : 畫面卡幀

為何 saveLayer 開銷大呢?

呼叫 saveLayer() 會要求一個不在畫面上呈現的緩衝區空間並將內容繪製在上面
而此時 GPU 會像是水管般的運作,Render switch 將強制 GPU 重新導入暫時的 stream 並且將其送回去
而在手機 GPU 中,這項操作會特別影響渲染的性能。

哪時候會需要 saveLayer 呢?

如果我們需要動態顯示大量的圖形,我們假設這些圖形皆來源自 Server
而這些圖形有些透明度並且可能相疊也可能不相疊
則此時我們便可能需要使用到 saveLayer()

當呼叫 saveLayer 情況發生時,如何 Debug ?

我們可以透過 DevTools 的時間線來偵測是否有直接或間接的呼叫 saveLayer()
可以開啟 DevTools 的 PerformanceOverlayLayer.checkerboardOffscreenLayers 來查看狀態
而這個按鈕在 Performance View 可以看得到

最小化的呼叫 saveLayer

如何避免呼叫 saveLayer 呢?

如果是來自我們程式碼的呼叫,或許我們能降低甚至不使用它們?
舉個例子,或許我們的 UI 重疊了兩個圖形,每個圖形皆擁有非零的透明度:
1. 如果他們總是同時出現,則我們可以透過預先計算以及快取等機制來避免呼叫 saveLayer
當我們可以預先計算時,便可以避免 saveLayer 的使用了
2. 又或者我們可以重構我們的程式邏輯來避免兩個圖片重疊在一起?

那如果是來自套件的呼叫的話,或許聯繫套件擁有者並討論為何這些呼叫是必須的?
是否有方式是可以降低或甚至不使用它們的?
如果沒辦法的話或許可以找尋其他套件或甚至是自己撰寫相同功能的套件來避免使用

而以下這些 Widget 也有可能會呼叫到 saveLayer


官網內文還有提及很多需要注意的地方
因為時間的關係或許不能一一介紹完畢
剩下的之後會找時間補上
也歡迎讀者閱讀後一起討論 ~


參考資料

強而有力的官網後盾


上一篇
通徹 Flutter 學習路徑 Day28 - 如果 Rust X Flutter
下一篇
通徹 Flutter 學習路徑 Day 30 - 完賽感想
系列文
通徹 Flutter 學習路徑30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言