So I was trying to use androidx.lifecycle:lifecycle-runtime-compose
to implement onStart
/onStop
events for my Composable screens:
In MainScreen I have:
LifecycleStartEffect(Unit) {
Timber.d("MainScreen LifeCycle ON_START ${System.currentTimeMillis()}")
onStopOrDispose {
// Provide the onStopOrDisposeEffect to the LifecycleStartEffect
// to run when the observer receives an (ON_STOP) Lifecycle.Event.ON_STOP
// event or must undergo cleanup.
// WILL BE CALLED TWICE on navigating to another screen
Timber.d("MainScreen LifeCycle ON_STOP ${System.currentTimeMillis()}")
}
}
When I navigate from this screen (MainScreen) to another one (e.g.: SettingsScreen)
@Composable
fun MainScreen(
...
navigateToSettings: () -> Unit,
) {
// navigation
NavHost(
navController = navController,
route = mainGraphRoutePattern,
startDestination = startDestination,
modifier = modifier
) {
mainScreen(
...
navigateToSettings = navController::navigateToSettings,
)
settingsScreen(
...
navigateUp = navController::navigateUpWithLifecycle
)
I notice that ON_STOP
is called two times:
19:54:43.918 D MainScreen LifeCycle ON_START 1706291683918
19:54:45.711 D MainScreen LifeCycle ON_STOP 1706291685710
19:54:46.566 D MainScreen LifeCycle ON_STOP 1706291686565
It's only called once if I'm not navigating to another screen but just leaving the screen (on back/home button pressed)
Then I decided to remove LifecycleStartEffect
and use LifecycleEventEffect
instead:
LifecycleEventEffect(event = Lifecycle.Event.ON_START) {
if (DEBUG) Timber.d("MainScreen LifeCycle ON_START ${System.currentTimeMillis()}")
}
LifecycleEventEffect(event = Lifecycle.Event.ON_STOP) {
if (DEBUG) Timber.d("MainScreen LifeCycle ON_STOP ${System.currentTimeMillis()}")
}
And now it works fine (ON_STOP
is called only once):
20:02:19.916 D MainScreen LifeCycle ON_START 1706292139916
20:02:33.860 D MainScreen LifeCycle ON_STOP 1706292153859
It has general implementation to observe events:
@Composable
fun LifecycleEventEffect(
event: Lifecycle.Event,
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
onEvent: () -> Unit
) {
if (event == Lifecycle.Event.ON_DESTROY) {
throw IllegalArgumentException("LifecycleEventEffect cannot be used to " +
"listen for Lifecycle.Event.ON_DESTROY, since Compose disposes of the " +
"composition before ON_DESTROY observers are invoked.")
}
// Safely update the current `onEvent` lambda when a new one is provided
val currentOnEvent by rememberUpdatedState(onEvent)
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, e ->
if (e == event) {
currentOnEvent()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
But what's wrong with LifecycleStartEffect
then? It's available in the same library as well.
Is it a bug in this implementation?
p.s. info about LifecycleStartEffect
: https://developer.android.com/jetpack/androidx/releases/lifecycle