My question is that when i navigate back/popup to previous composable screen Lifecycle.Event.ON_CREATE event call again. For example i have two composable screen, first show list of item and send one is detail screen of specific item. When i navigate back to list item screen. List item screen load(network call) again. Below is code test sample
Navigation Logic
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home"){
composable("home") {
RememberLifecycleEvent(event = {
Log.i("check","home event")
// API Call
})
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { navController.navigate("blur") }) {
Text(text = "Blur")
}
}
}
composable("blur") {
RememberLifecycleEvent(event = {
Log.i("check","blur event")
})
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { navController.navigate("home") }) {
Text(text = "Home")
}
}
}
}
Lifecycle Event Logic
@Composable
fun RememberLifecycleEvent(
event: () -> Unit,
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
) {
val state by rememberUpdatedState(newValue = event)
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { owner, event ->
if (event == Lifecycle.Event.ON_CREATE) {
state()
Log.i("check","event = $event")
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
I want to call api only first time in Lifecycle.Event.ON_CREATE event
CodePudding user response:
This is happening because when you navigate from A
to B
, the onDispose
is called in A
. Then, when you return to A
from B
, the DisposableEffect
is called again and since the Activity
is already in "resumed" state, the ON_CREATE
event is sent again.
My suggestion is controlling this call in your view model since it is kept alive after you go to B
from A
.
CodePudding user response:
There are a few possibilities depending if you want to call the API once on every 'forward' navigation to your first screen or if you want to call the API just once based on some other criteria.
If former, you can either use a ViewModel
and call the API from it when the ViewModel
is created. If you use Hilt and call hiltViewModel()
inside your Composable the ViewModel
will be scoped to the lifecycle of the NavBackStackEntry
of your NavHost
.
But the same scope can also be achieved by simply using a rememberSaveable
, since this will use the saveStateHandle
from the NavBackStackEntry
of your NavHost
.
Another advantage is that both of the above options also ensure that the API won't be called again on orientation change and other configuration changes (when they are enabled).
// Just a sample (suspend) call
suspend fun someApi(): String {
// ...
return "some result"
}
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home"){
composable("home") {
var apiCalled by rememberSaveable { mutableStateOf(false) }
if (!apiCalled) {
apiCalled = true
// key = Unit is okay here, we only want to launch once when entering the composition
LaunchedEffect(Unit) {
val result = runCatching {
someApi()
}
if (result.isFailure) {
// retry the api call? or report the error
}
}
}
// rest of your code ...
}
}