Я не совсем понимаю, как работает LauchedEffect, можете мне объяснить?
Вот контекст: я вызываю account.get(), чтобы узнать, существует ли пользовательский сеанс в моем приложении или нет, в зависимости от случая я возвращаю userExist = true или = false
Но во всех случаях он возвращает мне return = true, чтобы знать, что я должен запустить свой LaunchedEffect в этот момент, вот мой код
ВсплескУистате:
data class SplashUiState(
val result: Boolean = false,
val userExist: Boolean = false,
val error: List<Pair<String, String>> = emptyList()
)
Модель SplashView:
private val _state = MutableStateFlow(SplashUiState())
val state = _state.asStateFlow()
...
init {
delay(2000)
checkSessionExist()
}
...
private suspend fun checkSessionExist() {
when(val account = repository.getAccount()) {
is ResponseResult.Error -> {
Log.d("SplashViewModel", "${account.code}" + " ${account.message}")
_state.update { it.copy(result = true, userExist = false,
userError = listOf(
"code" to "${account.code}",
"message" to "${account.message}"
)
) }
}
is ResponseResult.Success -> {
Log.d("SplashViewModel", "${account.data}")
_state.update { it.copy(result = true, userExist = true) }
}
}
}
Заставка:
val state by viewModel.state.collectAsState()
LaunchedEffect(key1 = state.result) {
if (state.userExist) {
Log.d("LaunchedEffect", "user exist")
navController.navigate(AppRoutes.OVIDE_SCREEN) {
popUpTo(AppRoutes.SPLASH_SCREEN) {
inclusive = true
}
}
} else {
Log.d("LaunchedEffect", "user not exist")
navController.navigate(AppRoutes.WELCOME_SCREEN) {
popUpTo(AppRoutes.SPLASH_SCREEN) {
inclusive = true
}
}
}
}
Мой код кажется мне хорошим, хорошо структурированным, но когда пользователь существует userExist = true, он запускает мой код в else, а не в моем if.
Как это делается?
Строка кода
LaunchedEffect(key1 = state.result) {}
означает, что код внутри LaunchedEffect
будет выполнен
state.result
меняется.Это не означает, что LaunchedEffect
будет выполнено только тогда, когда значение state.result
станет true
. Таким образом, ваше приложение немедленно перейдет к WELCOME_SCREEN еще до того, как оно завершит проверку существования сеанса.
Пожалуйста, попробуйте изменить свой код следующим образом:
LaunchedEffect(key1 = state.result) {
if (state.result) {
if (state.userExist) {
Log.d("LaunchedEffect", "user exist")
navController.navigate(AppRoutes.OVIDE_SCREEN) {
popUpTo(AppRoutes.SPLASH_SCREEN) { inclusive = true }
}
} else {
Log.d("LaunchedEffect", "user not exist")
navController.navigate(AppRoutes.WELCOME_SCREEN) {
popUpTo(AppRoutes.SPLASH_SCREEN) { inclusive = true }
}
}
}
}
Это обеспечит навигацию после завершения загрузки.
«Это не означает, что LaunchedEffect будет выполнен только тогда, когда значение state.result станет истинным». Да, именно так я это и видел, спасибо!
Просмотрите предварительный просмотр в KDoc из LaunchedEffect:
Когда LaunchedEffect входит в композицию, он запускает блок в CoroutineContext композиции. Сопрограмма будет отменена и перезапущена, когда LaunchedEffect будет перекомпонован с другим ключом1.
Ваш checkSessionExist() всегда создает state.result == true. И это может быть причиной того, что ваш LaunchedEffect никогда не переходит в другое состояние.
Попробуйте следующий вызов LaunchedEffect(state) {} А также вставьте уже добавленное дополнительное предложение if @BenjyTec.
Кроме того, должно работать следующее:
LaunchedEffect(state) {
if (state.result) {
if (state.userExist) {
Log.d("LaunchedEffect", "user exist")
navController.navigate(AppRoutes.OVIDE_SCREEN) {
popUpTo(AppRoutes.SPLASH_SCREEN) { inclusive = true }
}
} else {
Log.d("LaunchedEffect", "user not exist")
navController.navigate(AppRoutes.WELCOME_SCREEN) {
popUpTo(AppRoutes.SPLASH_SCREEN) { inclusive = true }
}
}
}
}
LaunchedEffect remember
используется для запоминания и забывания лямбда-блока с RememberObserver
и coroutineScope
под капотом.
@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
key1: Any?,
block: suspend CoroutineScope.() -> Unit
) {
val applyContext = currentComposer.applyCoroutineContext
remember(key1) { LaunchedEffectImpl(applyContext, block) }
}
RememberObserver
интерфейс используется в LaunchedEffect для проверки того, когда запоминается блок кода. Это происходит, когда запоминание блока входит в композицию, и забывается, когда Composable или блок кода выходят из композиции.
@Suppress("CallbackName")
interface RememberObserver {
/**
* Called when this object is successfully remembered by a composition. This method is called on
* the composition's **apply thread.**
*/
fun onRemembered()
/**
* Called when this object is forgotten by a composition. This method is called on the
* composition's **apply thread.**
*/
fun onForgotten()
/**
* Called when this object is returned by the callback to `remember` but is not successfully
* remembered by a composition.
*/
fun onAbandoned()
}
Вы можете реализовать этот интерфейс самостоятельно для создания состояния или Composable для очистки или самостоятельно вызвать обратный вызов, например DisposableEffect. Я видел некоторые способы размещения растровых изображений в некоторых кодах.
И реализация LaunchedEffect использует его для отмены задания, которое оно создает как
internal class LaunchedEffectImpl(
parentCoroutineContext: CoroutineContext,
private val task: suspend CoroutineScope.() -> Unit
) : RememberObserver {
private val scope = CoroutineScope(parentCoroutineContext)
private var job: Job? = null
override fun onRemembered() {
// This should never happen but is left here for safety
job?.cancel("Old job was still running!")
job = scope.launch(block = task)
}
override fun onForgotten() {
job?.cancel(LeftCompositionCancellationException())
job = null
}
override fun onAbandoned() {
job?.cancel(LeftCompositionCancellationException())
job = null
}
}
По сути, LaunchedEffect
проверяет жизненный цикл композиции, реализуя RememberObserver
, и вызывает функции приостановки с помощью CoroutineScope.
Поскольку это запоминаемая функция, она сбрасывает свою лямбду или создает новое замыкание при изменении ключей.
DisposableEffects также проверяет onForgotten
, что отличается от LaunchedEffect в жизненном цикле компоновки.
private class DisposableEffectImpl(
private val effect: DisposableEffectScope.() -> DisposableEffectResult
) : RememberObserver {
private var onDispose: DisposableEffectResult? = null
override fun onRemembered() {
onDispose = InternalDisposableEffectScope.effect()
}
override fun onForgotten() {
onDispose?.dispose()
onDispose = null
}
override fun onAbandoned() {
// Nothing to do as [onRemembered] was not called.
}
}
Это действительно очень интересно и познавательно, большое спасибо, я собираюсь более подробно рассмотреть то, чем вы поделились.
@BenjyTec Конечно :)