聊聊Jetpack Compose的生命週期與狀態管理

前言
沒弄清楚的終究會來找自己。
近期在開發UI的過程中,經常遇到bug,不過也可以終歸是在使用UI的過程中,沒有詳讀好使用的說明書,所導致在開發過程中沒有精確的使用生命週期處理UI,甚至是使用某些物件去處理UI的更新
那麼,接下來這篇文章將會從三個重點下去統整UI的建構與邏輯:
- UI生命週期
- 改變UI的狀態管理工具
正文
JetPack Compose的UI生命週期
主要生命週期分成三種:
LaunchedEffect
範例:
@Composable
fun MyComposable(data: String) {
LaunchedEffect(data) {
println("LaunchedEffect triggered with $data")
}
}
特性:
- 第一次進入時會啟動LaunchedEffect的程式碼Scope
- Key變化的時候也會啟動LaunchedEffect的程式碼Scope
- 銷毀時會自動清理協程
DisposableEffect
範例:
@Composable
fun MyDisposableComposable(resource: String) {
DisposableEffect(resource) {
// 初始化资源
println("Resource initialized: $resource")
onDispose {
// 清理资源
println("Resource disposed: $resource")
}
}
}
基本行為:
- 第一次進入時,會執行初始化代碼
- Key變化時,會先調用onDispose,然後重新初始化
- 銷毀時,會自動調用onDispose。
SideEffect
範例:
@Composable
fun CounterWithSideEffect() {
var counter by remember { mutableStateOf(0) }
// 顯示一個計數器按鈕
Button(onClick = { counter++ }) {
Text("Counter: $counter")
}
// 使用 SideEffect 記錄計數器的值
SideEffect {
println("Counter updated to: $counter")
}
}
- 執行時機:SideEffect 在每次重組完成後觸發,但僅當該組合函數本身參與重組時執行。
- 應用場景:它的用途主要是處理那些需要與 Compose 狀態同步但不影響組合樹的副作用。
- 線程保證:SideEffect 保證執行在主線程上,因此適合執行 UI 操作或輕量級的副作用操作。
稍微統整一下三者的使用情境:

狀態管理工具
接著這邊這邊會討論一下刷新UI時的狀態管理工具,宣告式的UI寫法基本上都是藉由狀態綁定來更新UI,無論Jetpack Compose或者是SwiftUI都是這樣的響應式的寫法,接下來,就讓我們來一探究竟:
如果寫在Android的MVVM的View裡面,來實踐狀態管理的話
通常可以用兩種狀態管理工具來實作:
1.remember
特性:
a. 組合內部狀態管理工具:
- remember 用於在組合函數內記住狀態。
- 在組合期間,remember 的值不會丟失,但當組合函數被移除(如退出屏幕)時,其狀態也會被清理。
b. 僅限組合的生命周期:
- 狀態的生命周期與組合綁定,當組合被移除時,remember 的值會重置。
c.重組時保持狀態:
- 當組合函數重新組合時,remember 的值保持不變,不會重新計算。
適用場景的話,大致是臨時狀態管理:適合組合內部的短期狀態,例如按鈕點擊計數器,如果需要跨屏幕(比如push到下一頁的時候),這個狀態保存得值就會消失。
舉個例子:
@Composable
fun RememberExample() {
var counter by remember { mutableStateOf(0) } // 記住狀態
Button(onClick = { counter++ }) {
Text("Counter: $counter")
}
}
2. LiveData
特性:
a. 基於 ViewModel 的生命周期感知工具:
LiveData 是 Android 架構組件,與 ViewModel 配合使用,適合跨屏幕或長生命周期狀態管理。
b. 支持觀察者模式:
多個組合函數或非 Compose 組件(如 Fragment 或 Activity)可以觀察同一個 LiveData。
c. 屏幕旋轉後狀態保持:
由於 LiveData 存儲在 ViewModel 中,其狀態會在屏幕配置變化(如旋轉)後保留。
舉個例子:
class CounterViewModel : ViewModel() {
private val _counter = MutableLiveData(0)
val counter: LiveData<Int> get() = _counter
fun increment() {
_counter.value = (_counter.value ?: 0) + 1
}
}
@Composable
fun LiveDataExample(viewModel: CounterViewModel = viewModel()) {
val counter by viewModel.counter.observeAsState(0) // 將 LiveData 轉為 Compose 狀態
Button(onClick = { viewModel.increment() }) {
Text("Counter: $counter")
}
}
所以簡而言之,如果你的操作是短暫的,那就使用remember吧,如果你的操作是長期的,那就使用livedata吧
這邊也簡單比較一下兩者的使用:

那如果今天我是在MVVM架構下的ViewModel裡面儲存值呢?我應該要怎麼用來狀態管理呢?
推薦的狀態管理工具有三:MutableStateOf、MutableStateFlow、LiveData
1. mutableStateOf
特性:
a. mutableStateOf 是 Compose 提供的輕量級狀態容器。
b. 它本質上是 MutableState,屬於單一值的狀態。
c. 當狀態值改變時,直接觸發重組。
優點:
a. 簡單直接:不需要額外的工具或協程,適合管理簡單的狀態。
b. 與 Compose 高度集成:值改變時,直接觸發重組,無需手動觀察。
缺點:
a.不支持異步數據:無法處理數據流或多次發射的數據更新。
b.僅適用於單值狀態:不適合管理複雜的多屬性狀態。
適用場景
• 簡單同步狀態:
• 如按鈕計數器或表單輸入的單一值。
• 組合內的短生命周期狀態。
舉個例子:
class CounterViewModel : ViewModel() {
var counter by mutableStateOf(0)
private set
fun increment() {
counter++
}
}
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
val counter = viewModel.counter
Button(onClick = { viewModel.increment() }) {
Text("Counter: $counter")
}
}
2. MutableStateFlow
特性:
a. MutableStateFlow 是 Kotlin 的 StateFlow 的可變實現。
b. 它是 Flow 的一種,設計用來持有單一值並支持異步流。
c. 支持多觀察者,適合數據流場景。
優點:
a.支持異步操作:與協程結合良好,適合處理網路請求或連續數據流。
b.跨組件共享狀態:多個組合函數可以同時觀察同一個 StateFlow。
缺點:
a.需要協程支持:狀態更新需要運行在協程中。
b.稍微複雜:對於非常簡單的狀態管理,可能顯得冗餘。
適用場景:
• 異步數據流:
• 如網路請求的結果或頻繁更新的 UI 狀態。
• 需要多組件共享數據。
舉個例子:
class CounterViewModel : ViewModel() {
private val _counter = MutableStateFlow(0)
val counter: StateFlow<Int> = _counter
fun increment() {
_counter.value++
}
}
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
val counter by viewModel.counter.collectAsState()
Button(onClick = { viewModel.increment() }) {
Text("Counter: $counter")
}
}
3. LiveData
特性
a. LiveData 是 Android 架構組件的一部分,用於實現狀態的生命周期感知。
b. 它與 ViewModel 集成良好,適合傳統的 Android 開發模式。
優點
a.生命周期感知:自動停止對已銷毀的組件的更新。
b.傳統支持:可與非 Compose 組件(如 Activity、Fragment)一起使用。
缺點
a. 與 Compose 的集成不如 StateFlow 自然:需要用 observeAsState 包裝,間接性較強。
b. 不支持協程數據流:只能處理單一事件或狀態變化,對於連續更新的場景不太方便。
適用場景
• 傳統架構項目:適合需要同時支持 Compose 和非 Compose 界面的混合項目。
• 需要生命周期管理:當狀態需要根據視圖生命周期自動調整時。
舉個例子:
class CounterViewModel : ViewModel() {
private val _counter = MutableLiveData(0)
val counter: LiveData<Int> = _counter
fun increment() {
_counter.value = (_counter.value ?: 0) + 1
}
}
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
val counter by viewModel.counter.observeAsState(0)
Button(onClick = { viewModel.increment() }) {
Text("Counter: $counter")
}
}
看完上述的比較,這邊也簡單的彙整一下三者的差別:

嘛,所以通常我在實作Jetpack Compose的時候都會使用MutableStateOf、MutableStateFlow
大概搞懂這幾個項目,就可以實作蠻不錯的UI了。
尾聲
這部分還算是比較簡單的UI實作,往後的篇章可能會提到更多的RxJava在實作面配合UI的操作,我也是踩過不少坑,才走到今天的啊。