Công thức LocalLifecycleOwner
Công thức này minh hoạ cách sử dụng LifecycleResumeEffect trong 3 mục của thành phần Điều hướng để tạm dừng và tiếp tục công việc dựa trên trạng thái vòng đời của mục.
Cách hoạt động
Trong Navigation 3, theo mặc định, mỗi NavEntry sẽ được cung cấp LifecycleOwner riêng thông qua LocalLifecycleOwner.current. Điều này có nghĩa là mọi thành phần nhận biết vòng đời bên trong mục nhập đều tự động được đặt phạm vi thành NavEntry.
LifecycleResumeEffect bằng Cảnh hộp thoại
-
RouteA (Màn hình):
- Sử dụng
LifecycleResumeEffect(Unit)có phạm vi làLocalLifecycleOwner.currentcủaNavEntryđể đẩy nhanh tiến độLinearProgressIndicatortrong khi ở trạng tháiRESUMED. - Tự động đặt lại
progressValuevề0fbất cứ khi nào đạt đến1f.
- Sử dụng
-
RouteB (Hộp thoại):
- Được định cấu hình dưới dạng một hộp thoại bằng cách dùng
DialogSceneStrategy.dialog(). - Khi người dùng mở hộp thoại RouteB, RouteA vẫn hiển thị phía sau hộp thoại ở trạng thái
STARTED(rời khỏiRESUMED). LifecycleResumeEffectgọionPauseOrDispose, tạm dừng chỉ báo tiến trình.- Khi hộp thoại bị đóng, RouteA sẽ quay về
RESUMEDvàLifecycleResumeEffectsẽ tự động tiếp tục chỉ báo tiến trình.
- Được định cấu hình dưới dạng một hộp thoại bằng cách dùng
/* * Copyright 2026 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.nav3recipes.lifecycleowner import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.snap import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.LifecycleResumeEffect import androidx.lifecycle.compose.dropUnlessResumed import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberNavBackStack import androidx.navigation3.scene.DialogSceneStrategy import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.ui.setEdgeToEdgeConfig import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import kotlin.time.Duration.Companion.milliseconds @Serializable private data object RouteA : NavKey @Serializable private data object RouteB : NavKey class LifecycleOwnerActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = rememberNavBackStack(RouteA) val dialogStrategy = remember { DialogSceneStrategy<NavKey>() } NavDisplay( backStack = backStack, onBack = backStack::removeLastOrNull, sceneStrategies = listOf(dialogStrategy), entryProvider = entryProvider { entry<RouteA> { LogLifecycleEffect("ScreenA") ContentGreen("Screen A") { var progressValue by remember { mutableFloatStateOf(0f) } val coroutineScope = rememberCoroutineScope() val animatedProgress by animateFloatAsState( targetValue = progressValue, animationSpec = if (progressValue == 0f) snap() else tween(durationMillis = 100, easing = LinearEasing), label = "ProgressAnimation" ) // LifecycleResumeEffect runs only while ScreenA is in the RESUMED state. // When the dialog opens, ScreenA transitions to PAUSED, which calls onPauseOrDispose. LifecycleResumeEffect(Unit) { val job = coroutineScope.launch { while (true) { delay(100.milliseconds) progressValue += 0.01f if (progressValue >= 1f) { progressValue = 0f } } } onPauseOrDispose { job.cancel() } } Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(16.dp) ) { LinearProgressIndicator(progress = { animatedProgress }) Spacer(modifier = Modifier.height(16.dp)) Button(onClick = dropUnlessResumed { backStack.add(RouteB) }) { Text("Open Dialog") } } } } entry<RouteB>(metadata = DialogSceneStrategy.dialog()) { LogLifecycleEffect("Dialog") AlertDialog( onDismissRequest = backStack::removeLastOrNull, title = { Text("Dialog") }, text = { Text("Screen A is no longer in resumed state, pausing the progress indicator.") }, confirmButton = { Button(onClick = dropUnlessResumed { backStack.removeLastOrNull() }) { Text("Dismiss Dialog") } } ) } } ) } } } @Composable private fun LogLifecycleEffect(screenName: String) { LifecycleEventEffect(Lifecycle.Event.ON_CREATE) { Log.d("LifecycleOwner", "$screenName: ON_CREATE") } LifecycleEventEffect(Lifecycle.Event.ON_START) { Log.d("LifecycleOwner", "$screenName: ON_START") } LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { Log.d("LifecycleOwner", "$screenName: ON_RESUME") } LifecycleEventEffect(Lifecycle.Event.ON_PAUSE) { Log.d("LifecycleOwner", "$screenName: ON_PAUSE") } LifecycleEventEffect(Lifecycle.Event.ON_STOP) { Log.d("LifecycleOwner", "$screenName: ON_STOP") } // Note that ON_DESTROY event is not observable from composables. }