Flutter調優--深入探究MediaQuery引起界面Rebuild的原因及解決辦法

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/05/29/17440872.html
-Advertisement-
Play Games

app界面逐漸複雜時,我們不得不考慮去優化界面性能。本文中介紹的例子在開發中是很常見的,如果不瞭解MediaQuery.of的機制,可能會引起大量使用此方法的界面發生重繪操作,造成頁面卡頓、幀率下降。我們詳細分析了背後的源碼邏輯,介紹瞭解決辦法,希望能給大家的調優工作提供些許幫助。 ...


前言

我們可以通過MediaQuery.of(context)方法獲取到一些設備和系統的相關信息,比如狀態欄的高度、當前是否是黑暗模式等等,使用起來相當方便,但是也要註意可能引起的頁面rebuild問題。本文會介我們可以通過MediaQuery.of(context)方法獲取到一些設備和系統的相關信息,比如狀態欄的高度、當前是否是黑暗模式等等,使用起來相當方便,但是也要註意可能引起的頁面rebuild問題。本文會介紹一個典型的例子,並深入源碼來探討引起rebuild的原因,最後介紹避免rebuild的幾個辦法。
紹一個典型的例子,並深入源碼來探討引起rebuild的原因,最後介紹避免rebuild的幾個辦法。

典型例子

以快遞App中的查快遞場景舉例,首頁用MediaQuery.of(context).padding.top獲取了狀態欄高度,用戶點擊“查快遞”按鈕會跳轉到查快遞界面,在查快遞界面,用戶輸入單號可進行查詢操作。

99ee34767149efcdb4c81258d84649c76ec7d0b5.jpeg
當首頁的build方法被調用時,會輸出我們提前加好的日誌。我們發現,當查快遞界面的鍵盤彈出時,首頁的build方法被調用了多次:

1c71b25e3294787a554ff654302a1950b05f1e2c.png

主界面的build代碼如下:

0e60e8a0008a952db4058f2f6c7c7f4d2156d1bf.png

源碼探究

既然是因為主界面在build方法里使用了MediaQuery.of(context),從而導致當鍵盤彈出/隱藏時進行rebuild操作,那麼就先來看下MediaQuery類。

MediaQuery

ca93e1de11acadcdacc1be8bf1621c53ab4d4e67.png

其繼承自InheritedWidget,自身並沒有重寫createElement方法,從flutter三棵樹的角度講,對應的Element即為InheritedElement。有兩個屬性,data和child,我們可以從data中獲取一些設備/系統相關的屬性。

另外還有兩個比較重要的方法:

fromWindow(key : Key, child : Widget)

5b724d4ff9ae1364473ce47a81a5c0c4fcd247ef.png

此方法直接返回_MediaQueryFromWindow對象,後面會詳細介紹。

of(context : BuildContext)

50.png

方法里調用了dependOnInheritedWidgetOfExactType,接下來我們詳細分析下背後的調用流程。

MediaQuery.of(context) 調用流程

入參是context,本例中的主界面是StatelessWidget,那麼這裡的context便是StatelessElement。整體調用流程如下:

845592be3d798d7f664f343d1c3bc2e28b2ed114.png

dependOnInheritedWidgetOfExactType

0921532a03e10a237cce55d11b688cac76e3744e.png

_inheritedWidgets列表中查詢是否有MediaQuery類型的InheritedElement,從三棵樹的角度講,就是從當前節點一直向上查找,找到最近的MediaQuery控制項。如果找到,則調用dependOnInheritedElement方法(一般情況下是一定能找到的,下麵再詳細介紹)。

dependOnInheritedElement

3475047fddc5392b520ed244eaf9bde411a21349.png

此方法負責將找到的InheritedElement(也就是MediaQuery對應的Element)存起來,並且調用InheritedElement#updateDependencies方法。

updateDependencies

fc0db47c49a1f82bde4797e316087c40bda77d35.png

setDependencies

a33b8a6c17284ec860c808104a5e9b5796a3e247.png

最後兩個方法很簡單,其作用是將主頁對應的StatelessElement存儲到了MediaQuery對應的InheritedElement#_dependents中。

研究完MediaQuery.of(context)背後的原理,我們可以知道:通過調用of方法,主界面對應的ElementMediaQuery建立了綁定關係,MediaQuery對應的InheritedElement存儲了主界面Element的引用。

Rebuild起點

當介紹dependOnInheritedWidgetOfExactType方法時,我們提道:從當前節點往父節點尋找,一般情況下是一定能找到的MediaQuery控制項的。這是因為在WidgetsApp里會自動給我們創建一個根MediaQuery

main方法里,無論使用CupertinoApp還是MaterialApp,最後都會在內部創建WidgetsApp。我們直接看_WidgetsAppState#build方法里的一個代碼片段:

b99a11825cca09b3db11d7d08c0f2f7e97b1b83a.png

會首先檢查widget.useInheritedMediaQuery,這個屬性預設為false。如果你創建MaterialApp/CupertinoApp時,沒有設置useInheritedMediaQuery屬性,或者設置了這個屬性為null,但找不到MediaQueryData,那麼這裡就會調用MediaQuery.fromWindow方法。

上面介紹MediaQuery#fromWindow時,我們知道它會創建_MediaQueryFromWindow控制項。

628215f6ff6f55a7e4253fd82aec42af96564435.png

_MediaQueryFromWindow的代碼不是很多,把和本文相關的代碼全部貼出來了,大家可以自己看下,代碼如上圖所示。

build方法里創建了MediaQuery控制項,並實現了didChangeMetrics方法,當手機發生旋轉、鍵盤彈出/隱藏時就會調用此方法,didChangeMetrics內部又調用了setSate,從而導致build方法被重新調用。

通過flutter三顆樹的原理我們可以知道,上述所說的“build方法被重新調用”涉及到MediaQueryFromWindow對應的ElementupdateChild方法,簡單看下updateChild的內部處理規則:

a33b8a6c17284ec860c808104a5e9b5796a3e247.png

對MediaQueryFromWindow而言,每次都會創建新的MediaQuery Widget,根據Element#updateChild源碼(不是本文討論重點,不再詳細分析其源碼)得知,最終會調用MediaQuery對應的Element的update方法。

經過一系列的跳轉過後,最終會調用到下麵的兩個核心方法:

beb27e10f74c988d10e0de4fda450f52aec77be7.png

上面介紹的MediaQuery.of(context)方法最終會把入參Context放到_dependents變數里,而這裡會遍歷這個map,調用每一個ContextdidChangeDependecies方法,didChangeDependecies會將此Context置為dirty狀態,下一幀來臨時會被重新繪製,並調用此Contextbuild方法。

所以,破案了,當鍵盤彈起/隱藏時快遞主頁會被rebuild的原因找到了!

整體的rebuild調用流程如下,感興趣的可以結合這個調用流程圖去看源碼:

bf648f2c2399def55a9fdb1a20260ef2fe858394.png

避免rebuild的辦法

研究過源碼後,解決方案就變的很簡單。

  • 自定義useInheritedMediaQuery屬性為true,併在最外麵包一層MediaQuery,讓WidgetsApp創建時使用MediaQuery,而不去使用監聽了application尺寸變化的_MediaQueryFromWindow控制項。

f048696ba22681d3cefab511748656b38a3d1098.png

  • 避免在頁面中使用MediaQuery.of(context)方法,可以使用對應的替代方法,比如本例可以採用下麵的代碼進行替代,註意單位的轉換。

17ebf14ed1e437106cd05b5c17f2da2aeb815213.png

  • 如果必須要使用MediaQuery.of(context)方法,可以使用Builder控制項包裹下,of方法的入參傳入此Buildercontext即可,這樣被rebuild僅是Builder控制項包裹下的widget子樹。

5e9bafc27455cbba7c69653d1052f73c1ed71fa5.png

總結

app界面逐漸複雜時,我們不得不考慮去優化界面性能。本文中介紹的例子在開發中是很常見的,如果不瞭解MediaQuery.of的機制,可能會引起大量使用此方法的界面發生重繪操作,造成頁面卡頓、幀率下降。我們詳細分析了背後的源碼邏輯,介紹瞭解決辦法,希望能給大家的調優工作提供些許幫助。

作者:京東物流 沈明亮

來源:京東雲開發者社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我記得之前我採集、提取過兩份菜譜資料資料庫,一份是菜譜數據不上萬的帶圖片的,詳情點擊這裡,一份是2萬多條記錄但格式未整理並且沒有圖片的。而今天的這個3萬多條含圖片的菜譜資料ACCESS資料庫包含了3萬多條常菜譜做法,並且結構簡潔,最主要的是每個菜譜都包含圖片。 菜譜分50個大類和400多種小類,大類 ...
  • 一個學語文的朋友問我有沒有關於中華典故以及萬物由來的數據,我看了看手頭的資料發現還真沒有,而且網上似乎也沒有這一類的成品,因此就用程式採集了一個中華典故網的文章。 分類統計情況有:成語典故(共4198條)、典故雜聞(共702條)、國外典故(共29條)、科學典故(共29條)、歷史典故(共374條)、神 ...
  • 本系列為:MySQL資料庫詳解,為千鋒資深教學老師獨家創作,致力於為大家講解清晰MySQL資料庫相關知識點,含有豐富的代碼案例及講解。如果感覺對大家有幫助的話,可以【關註】持續追更 ...
  • 《公務員考試基礎知識題庫ACCESS資料庫》搜集了大量公務員考試試題,包括公共基礎知識試題、行政職業能力試題、法律基礎知識試題、公安基礎知識試題等。 分類記錄統計情況為:法律基礎知識試題(共1359條)、公安基礎知識試題(共1709條)、公共基礎知識試題(共2905條)、行政職業能力試題(共6613 ...
  • 《求職面試寶典大全ACCESS資料庫》包含:1-穿著打扮、2-隨身攜帶、3-場景指導、4-考官分析、5-考官問題、6-回答參考表、7-面試大全。雖然有些表的記錄數不多,但勝在信息全面以及考官問題及答案的全面。 問題類型記錄數為:待遇問題(10條)、個人素養(10條)、教育家庭(10條)、經驗問題(1 ...
  • 因為簽於網站笑話不是採用微博型(一句或兩句短篇可以採用250長度的文本型存儲),而是所以文章內容型,所以內容保存的欄位是 Microsoft Access 資料庫里的備註類型。 資料庫內容經過整理,格式比較統一,比如不會有多餘的段落或不整齊的段落;分類比較集中,只有爆笑男女(305)、兒童笑話(59 ...
  • [TOC](快速上手kettle二 小試牛刀) ### 一 、前言 上一期中大概介紹了下kettle,並已經把kettle下載安裝完了。 這一期我們就來簡單體驗下kettle怎麼進行數據轉換的。 ### 二 、兩個小目標 我們這裡呢就以兩個小案例來體驗下kettle - 將csv文件通過kettle ...
  • 這是從互聯網上搜集的各種初中語文作文20000篇,經過完美的格式內容整理,包含大量《中考滿分作文》及《中考作文指導》資料,資料庫按年級和分類可以快速的為初中作文提供最齊全的參考。 年級包含:初中二年級(2377)、初中三年級(1988)、初中一年級(15465)。分類包含:讀後感(778)、話題(1 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:本文代碼示例演示瞭如何在WPF中使用LiveCharts庫創建動態條形圖。通過創建數據模型、ViewModel和在XAML中使用`CartesianChart`控制項,你可以輕鬆實現圖表的數據綁定和動態更新。我將通過清晰的步驟指南包括詳細的中文註釋,幫助你快速理解並應用這一功能。 先上效果: 在 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • 概述:本示例演示了在WPF應用程式中實現多語言支持的詳細步驟。通過資源字典和數據綁定,以及使用語言管理器類,應用程式能夠在運行時動態切換語言。這種方法使得多語言支持更加靈活,便於維護,同時提供清晰的代碼結構。 在WPF中實現多語言的一種常見方法是使用資源字典和數據綁定。以下是一個詳細的步驟和示例源代 ...
  • 描述(做一個簡單的記錄): 事件(event)的本質是一個委托;(聲明一個事件: public event TestDelegate eventTest;) 委托(delegate)可以理解為一個符合某種簽名的方法類型;比如:TestDelegate委托的返回數據類型為string,參數為 int和 ...
  • 1、AOT適合場景 Aot適合工具類型的項目使用,優點禁止反編 ,第一次啟動快,業務型項目或者反射多的項目不適合用AOT AOT更新記錄: 實實在在經過實踐的AOT ORM 5.1.4.117 +支持AOT 5.1.4.123 +支持CodeFirst和非同步方法 5.1.4.129-preview1 ...
  • 總說周知,UWP 是運行在沙盒裡面的,所有許可權都有嚴格限制,和沙盒外交互也需要特殊的通道,所以從根本杜絕了 UWP 毒瘤的存在。但是實際上 UWP 只是一個應用模型,本身是沒有什麼許可權管理的,許可權管理全靠 App Container 沙盒控制,如果我們脫離了這個沙盒,UWP 就會放飛自我了。那麼有沒... ...
  • 目錄條款17:讓介面容易被正確使用,不易被誤用(Make interfaces easy to use correctly and hard to use incorrectly)限制類型和值規定能做和不能做的事提供行為一致的介面條款19:設計class猶如設計type(Treat class de ...
  • title: 從零開始:Django項目的創建與配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 後端開發 tags: Django WebDev Python ORM Security Deployment Op ...
  • 1、BOM對象 BOM:Broswer object model,即瀏覽器提供我們開發者在javascript用於操作瀏覽器的對象。 1.1、window對象 視窗方法 // BOM Browser object model 瀏覽器對象模型 // js中最大的一個對象.整個瀏覽器視窗出現的所有東西都 ...