Flutter - 生命週期

StatefulWidget 須要藉助於 State 對象,在特定的階段來處理用戶的交互或其內部數據的變化,並體如今 UI 上。這個特定的階段,就涵蓋來一個組件從加載到卸載的全過程,即生命週期。Flutter 中的 Widget 也存在生命週期,而且經過 State 來體現。安全

而 App 則是一個特殊的 Widget。除了須要處理視圖顯示的各個階段(即視圖的生命週期)以外,還須要應對應用從啓動到退出所經歷的各個狀態(App 的生命週期)。app

State 生命週期

State 的生命週期,指的是在用戶參與的狀況下,其關聯的 Widget 所經歷的,從建立到顯示再到更新最後到中止,直至銷燬等各個過程階段。State 的生命週期能夠分爲 3 個階段:建立(插入視圖樹)、更新(在視圖樹中存在)、銷燬(從視圖樹中移除)。async

建立

State 初始化時會依次執行:構造方法 -> initState -> disChangeDependencies -> build,隨後完成頁面渲染。ide

初始化過程當中每一個方法的意義以下:函數

  • 構造方法是 State 生命週期的起點,Flutter 會經過調用 StatefulWidget.createState() 來建立一個 State。能夠經過構造方法,來接收父 Widget 傳遞的初始化 UI 配置數據。這些配置數據,決定了 Widget 最初的呈現效果。
  • initState,會在 State 對象被插入視圖樹的時候調用。這個函數在 State 的生命週期中只會被調用一次,因此能夠在這裏作一些初始化工做,好比爲狀態變量設定默認值。
  • didChangeDependencies 則來專門處理 State 對象依賴關係變化,會在 initState() 調用結束後,被 Flutter 調用。
  • build,做用是構建視圖。通過以上步驟,Framework 認爲 State 已經準備好了,因而調用 build。能夠在這個函數中,根據父 Widget 傳遞過來的初始化配置數據,以及 State 的當前狀態,建立一個 Widget 而後返回。

更新

Widget 的狀態更新,主要由 3 個方法觸發:setState、didchangeDependencies 與 didUpdateWidget.oop

如上三個方法對應的調用場景:post

  • setState:當狀態數據發生變化時,經過調用這個方法告訴 Flutter:「這兒的數據變了,請使用更新後的數據重建 UI!」。
  • didChangeDependencies:State 對象的依賴關係發生變化後,Flutter 會回調這個方法,隨後觸發組建構建。典型的場景是,系統語言 Locale 或應用主題改變時,系統會通知 State 執行 didChangeDependencies 回調方法。
  • didUpdateWidget:當 Widget 的配置發生變化時,好比,父 Widget 觸發重建(即父 Widget 的狀態發生變化時),熱重載時,系統會調用這個函數
一旦這三個方法被調用,Flutter 隨後就會銷燬老 Widget,並調用 build 方法重建 widget。

銷燬

組件銷燬比較簡單。好比組件被移除,或是頁面銷燬的時候,系統會調用 deactivate 和 dispose 這兩個方法,來移除或銷燬組件。ui

具體調用機制以下:this

  • 當組件的可見狀態發生變化時,deactivate 函數會被調用,這時 State 會被暫時從視圖樹中移除。值得注意的是,頁面切換時,因爲 State 對象在視圖樹中的位置發生了變化,須要先暫時移除後再從新添加,從新觸發組件構建,所以這個函數也會被調用。
  • 當 Sate 被永久地從視圖樹中移除時,Flutter 會調用 dispose 函數。而一旦到這個階段,組件就要被銷燬了,因此咱們能夠在這裏進行最終的資源釋放、移除監聽、清理環境,等等。

從功能、調用時機和調用次數的緯度總結這些方法以下;spa

方法名 功能 調用時機 調用次數
構造方法 接收父Widget傳遞的初始化UI配置數據 建立State時 1
initState 與渲染相關的初始化工做 在State被插入視圖樹時 1
didChangeDependencies 處理State隊形依賴關係變化 initState後及State對象依賴關係變化時 >= 1
build 構建視圖 State準備好數據須要渲染時 >= 1
setState 觸發視圖重建 須要刷新UI時 >= 1
didUpdateWidget 處理Widget的配置變化 父Widget setState觸發子Widget重建時 >= 1
deactivate 組件被移除 組件不可視 >= 1
dispose 組件被銷燬 組件被永久移除 1

App 生命週期

視圖的生命週期,定義了視圖的加載到構建的全過程,其回調機制可以確保咱們能夠根據視圖的狀態選擇合適的時機作恰當的事情。App 的生命週期,則定義了 App 從啓動到退出的全過程。

在原生 Android、iOS 開發中,有時須要在對應的 App 生命週期事件中作相應處理,好比 App 從後臺進入前臺、從前臺退到後臺,或是在 UI 繪製完成後作一些處理。

在原生開發中,能夠經過重寫 Activity、ViewController 生命週期回調方法,或是註冊應用程序的相關通知,來監聽 App 的生命週期並作相應的處理。而在 Flutter 中,咱們能夠利用 WidgetsBindingObserver 類,來實現一樣的需求。

WidgetsBindingObserver 中具體回調函數有以下一些方法:

abstract class WidgetsBindingObserver { // 頁面 pop Future<bool> didPopRoute() => Future<bool>.value(false); // 頁面 push Future<bool> didPushRoute(String route) => Future<bool>.value(false); // 系統窗口相關改變回調,如旋轉 void didChangeMetrics() { } // 文本縮放係數變化 void didChangeTextScaleFactor() { } // 系統亮度變化 void didChangePlatformBrightness() { } // 本地化語言變化 void didChangeLocales(List<Locale> locale) { } //App 生命週期變化 void didChangeAppLifecycleState(AppLifecycleState state) { } // 內存警告回調 void didHaveMemoryPressure() { } //Accessibility 相關特性回調 void didChangeAccessibilityFeatures() {} } 
其餘回調相對簡單,能夠查看官方文檔

生命週期回調

didChangeAppLifecycleState 回調函數中,有一個參數類型爲 AppLifecycleState 的枚舉類,這個枚舉類是 Flutter 對 App 生命週期狀態的封裝。它的經常使用狀態包括 resumed、inactive、paused 這三個。

  • resumed:可見的,並能響應用戶的輸入;
  • inactive:處在不活動狀態,沒法處理用戶響應;
  • paused:不可見並不能響應用戶的輸入,可是在後臺繼續活動中。

案例分享:在 initState 時註冊來監聽器,在 didChangeAppLifecycleState 回調方法中打印來當前的 App 狀態,最後在 dispose 時把監聽器移除:

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver { ... @override @mustCallSuper void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); // 註冊監聽器 } @override @mustCallSuper void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); // 移除監聽器 } @override void didChangeAppLifecycleState(AppLifecycleState state) async { print("$state"); if (state == AppLifecycleState.resumed) { // do sth } } } 

嘗試這切換一下前、後臺,觀察控制檯輸出的 App 狀態,能夠發現:

  • 從後臺切入前臺,控制檯打印的 App 生命週期變化以下:AppLifecycleState.paused -> AppLifecycleState.inactive -> AppLifecycleState.resumed;
  • 從前臺退回後臺,控制檯打印的 App 生命週期變化則變成了:AppLifecycleState.resumed -> AppLifecycleState.inactive -> AppLifecycleState.paused

幀繪製回調

除了須要監聽 App 的生命週期回調作相應的處理以外,有時候還須要在組件渲染以後作一些與顯示安全相關的操做。

在 iOS 開發中,能夠經過 dispatch_async(dispatch_get_main_queue(),^{...}) 方法,讓操做在下一個 RunLoop 執行;在 Android 開發中,能夠經過 View.post() 插入消息隊列,來保證在組件渲染後進行相關操做。

在 Flutter 中實現一樣的需求會更簡單: 依然使用萬能的 WidgetsBinding 來實現。

WidgetsBinding 提供了單次 Frame 繪製回調,以及實時 Frame 繪製回調兩種機制,來分別知足不一樣的需求:

  • 單次 Frame 繪製回調,經過 addPostFrameCallback 實現。它會在當前 Frame 繪製完成後進行回調,並只會回調一次,若是要再次監聽則須要再設置一次。
WidgetsBinding.instance.addPostFrameCallback((_){ print(" 單次 Frame 繪製回調"); // 只回調一次 }); 
  • 實時 Frame 繪製回調,則經過 addPersistentFrameCallback 實現。這個函數會在每次繪製 Frame 結束後進行回調,能夠用做 FPS 檢測。
WidgetsBinding.instance.addPersistentFrameCallback((_){ print(" 實時 Frame 繪製回調"); // 每幀都回調 });
相關文章
相關標籤/搜索