Android Lifecycle 生命週期感知元件

在 Android 開發中,管理 Activity 或 Fragment 的生命週期 (Lifecycle) 是一項核心且具備挑戰性的任務。傳統的做法是將大量的邏輯,例如連線啟動、資料監聽、資源釋放等,直接寫在 Activity 的生命週期回呼方法(如 onStart(), onStop())中。

這種傳統寫法常會導致以下問題:

  • 程式碼臃腫且耦合度高:Activity 肩負過多職責,邏輯難以重用與測試。
  • 資源洩漏風險:如果忘記在對應的生命週期釋放資源(例如在 onStop 忘記斷開資料庫連線),會導致記憶體洩漏 (Memory Leak) 或應用程式崩潰。

Android Lifecycle Library 透過觀察者模式 (Observer Pattern) 解決了這個痛點,讓元件具備「生命週期感知能力」,實現邏輯的解耦與自我管理。

關鍵角色

  • LifecycleOwner:擁有生命週期的物件,最常見的就是 Activity 和 Fragment。
  • LifecycleObserver:生命週期的觀察者,用來監聽 LifecycleOwner 的狀態變化並執行相應邏輯。

生命週期事件 (Events) 與狀態 (States)

理解生命週期的關鍵在於區分「事件」與「狀態」。你可以將其想像成一個狀態機的轉換過程:

  • Event (事件):框架觸發的「轉換動作」。例如 ON_START 表示轉換開始。
  • State (狀態):轉換完成後的「當前處境」。例如 STARTED 表示元件已就緒。

Android Activity Lifecycle

事件 (Events)說明狀態改變 (States)
ON_CREATE元件初始化完成。進入 CREATED
ON_START元件即將在螢幕上出現。進入 STARTED
ON_RESUME元件已在最前景,可供使用者互動。進入 RESUMED
ON_PAUSE元件失去焦點,但仍部分可見。保持 STARTED
ON_STOP元件完全不可見。回到 CREATED
ON_DESTROY元件被銷毀,資源應即刻釋放。進入 DESTROYED

實作生命週期觀察者

雖然早期我們常用 LifecycleEventObserver,但現在 Android 官方更推薦使用 DefaultLifecycleObserver,它的架構更清晰且易於維護。

使用 DefaultLifecycleObserver (推薦)

透過實作 DefaultLifecycleObserver,你可以只覆寫你需要的生命週期方法:

// 定義一個具備生命週期感知的播放器觀察者
class MyVideoObserver(private val player: VideoPlayer) : DefaultLifecycleObserver {
    
    override fun onResume(owner: LifecycleOwner) {
        // 當 Activity 回到前台,自動開始播放
        player.play()
    }

    override fun onPause(owner: LifecycleOwner) {
        // 當 Activity 進入背景,自動暫停以節省資源
        player.pause()
    }
    
    override fun onDestroy(owner: LifecycleOwner) {
        // 當 Activity 徹底銷毀,完全釋放資源
        player.release()
    }
}

註冊觀察者

在 Activity 或 Fragment 中,只需透過 lifecycle.addObserver() 即可完成綁定:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 註冊觀察者後,播放器邏輯將自動與 Activity 生命週期同步
        lifecycle.addObserver(MyVideoObserver(player))
    }
}

在 Jetpack Compose 中整合

在 Compose 開發中,組件(Composable)通常需要根據生命週期執行 Side Effects,這時我們會結合 Side Effect API 來處理。

LocalLifecycleOwner

在 Composable 函式中,你可以透過 LocalLifecycleOwner.current 快速取得當前的生命週期持有者。

使用 DisposableEffect 進行監聽

適用於需要手動註冊/移除監聽器的場景,例如統計埋點或特定硬體 API:

@Composable
fun LifecycleTracker(lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current) {
    
    DisposableEffect(lifecycleOwner) {
        // 建立一個臨時觀察者
        val observer = object : DefaultLifecycleObserver {
            override fun onStart(owner: LifecycleOwner) {
                println("使用者進入了畫面")
            }
        }

        // 註冊觀察者
        lifecycleOwner.lifecycle.addObserver(observer)

        // 清理機制:當 Composable 被移除或 lifecycleOwner 改變時執行
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
}

數據流的生命週期安全

在 UI 層面收集資料流(如 Flow)時,必須確保在背景時停止收集,以避免不必要的 CPU 與電力消耗。

repeatOnLifecycle

repeatOnLifecycle 讓 Flow 的收集能與 Activity/Fragment 的生命週期「同步」。它會在你指定的生命週期狀態(例如 STARTED)下啟動協程,並在離開該狀態時自動取消:

  • 當進入 onStart 時: 自動啟動協程,開始 collect 資料。
  • 當進入 onStop 時: 自動取消協程,停止收集,節省資源。
  • 當再次回到 onStart 時: 重新啟動協程,再次開始收集。
@Composable
fun ProfileScreen(viewModel: ProfileViewModel) {
    val lifecycleOwner = LocalLifecycleOwner.current

    LaunchedEffect(lifecycleOwner) {
        // 當生命週期至少處於 STARTED 時,執行區塊
        // 當生命週期進入 STOPPED 時,區塊內的協程會被自動取消
        lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // 在這裡安全地收集 Flow
            viewModel.userEvents.collect { event ->
                // 只有在 UI 可見時才處理事件
            }
        }
    }
}

collectAsStateWithLifecycle (極力推薦)

這是目前最推薦將 Flow 轉為 Compose State 的方法。它內部封裝了 repeatOnLifecycle 的邏輯,使用最為簡便。

使用它需要先確保你的 build.gradle 有導入以下依賴:

implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.0") // 或更新版本

應用範例:

// ViewModel 部分
class UserViewModel : ViewModel() {
    private val _userName = MutableStateFlow("載入中...")
    val userName: StateFlow<String> = _userName.asStateFlow()

    fun updateName(newName: String) {
        _userName.value = newName
    }
}

// Compose UI 部分
@Composable
fun UserProfileScreen(viewModel: UserViewModel = viewModel()) {
    // 關鍵行:以生命週期感知的方式收集 StateFlow
    // 當 App 進入背景時,它會自動停止收集以節省資源
    val name by viewModel.userName.collectAsStateWithLifecycle()

    Column(modifier = Modifier.padding(16.dp)) {
        Text(text = "目前使用者: $name")
        Button(onClick = { viewModel.updateName("Gemini") }) {
            Text("更改名字")
        }
    }
}
  • 當 App 進入背景(不可見)時,它會自動暫停上游節點的資料發射。
  • 當 App 回到前台時,它會自動恢復收集,確保資料永遠是最新的且節省資源。

生命週期開發最佳實務

  1. 邏輯與 Activity 分離:將與生命週期相關的業務邏輯封裝在 LifecycleObserver 中,保持 Activity 的簡潔。
  2. ViewModel 不應感知 UI 生命週期:ViewModel 應該是純粹的資料提供者,不應直接持有 LifecycleOwner 或觀察 UI 狀態。
  3. 成對操作:如果需要在 ON_START 初始化資源,務必在 ON_STOP 進行回收;ON_RESUME 則對應 ON_PAUSE
  4. 優先使用 collectAsStateWithLifecycle:這是處理 Compose 資料流最穩定且效能最佳的實踐方式。