admin管理员组

文章数量:1129461

I am implementing a LivenessScreen in Jetpack Compose with the following code:

LivenessScreen(
    viewModel = livenessViewModel,
    onErrorScreen = onErrorScreen,
    onLivenessComplete = onLivenessComplete
)

Problem: When I pass onErrorScreen and onLivenessComplete as lambda functions to the LivenessScreen, the UiState.Failure case is triggered multiple times under the following conditions:

  1. When the API fails, the failure case is triggered three times.
  2. When I press the back button from the system, the failure case is triggered again.

However, when I remove onErrorScreen and onLivenessComplete from the LivenessScreen parameters, the UiState.Failure is triggered only once when the API fails and is not triggered again when pressing the back button.

Observations:

  1. From the UseCase, the failure state is emitted only once according to the logs.
  2. The repeated triggers happen only when the lambdas are passed to the LivenessScreen.

Questions:

  • What could be causing the UiState.Failure case to be triggered multiple times?
  • How can I prevent the repeated triggering of the failure state when using lambdas in Jetpack Compose?
  • Are there any best practices to handle lambdas and state management to avoid such issues?

Any help or guidance would be greatly appreciated!

MainActivity.kt

setContent {
            val navController = rememberNavController()
            NavGraph(navController)
           
        }

NavGraph.kt

@Composable
    fun NavGraph(navController: NavHostController) {
        NavHost(
            navController = navController, startDestination = NavRoute.LiveNess.path
        ) {
            addLivenessScreen(navController, this)
    
            addLiveNessSuccessScreen(navController, this)
        }
    }


fun addLivenessScreen(navController: NavHostController, navGraphBuilder: NavGraphBuilder) {
    navGraphBuilderposable(route = NavRoute.LiveNess.path) {
        LivenessRoute(
            onLivenessComplete = {
                navController.navigate(NavRoute.LiveNessSuccess.path)
            },
            onErrorScreen = {
                navController.navigate(NavRoute.GenericErrorScreen.path)
            },
        )
    }
}

fun addLiveNessSuccessScreen(
    navController: NavHostController,
    navGraphBuilder: NavGraphBuilder,
    config: SDKConfiguration
) {
    navGraphBuilderposable(route = NavRoute.LiveNessSuccess.path) {
        LivenessSuccessScreen(
        )
    }
}

LivenessRoute.kt

@Composable
fun LivenessRoute(
    onLivenessComplete: () -> Unit,
    onErrorScreen: () -> Unit
) {
    val livenessViewModel: LivenessViewModel = koinViewModel()
    LivenessScreen(
        viewModel = livenessViewModel,
        onErrorScreen = onErrorScreen,
        onLivenessComplete = onLivenessComplete
    )
}

LivenessScreen.kt

@Composable
fun LivenessScreen(
    modifier: Modifier = Modifier,
    onLivenessComplete: () -> Unit,
    onErrorScreen: () -> Unit,
    viewModel: LivenessViewModel
) {
    val uiState = viewModel.uiState.collectAsStateWithLifecycle()
    V2Theme {
        Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(innerPadding),// Padding for better spacing
                contentAlignment = Alignment.Center // Center content within the Box
            ) {
                when (val state = uiState.value) {
                    is UiState.Loading -> {
                        Column(
                            horizontalAlignment = Alignment.CenterHorizontally, // Center items horizontally
                            verticalArrangement = Arrangement.Center // Center items vertically
                        ) {
                            CircularProgressIndicator()
                            Spacer(modifier = Modifier.height(16.dp))
                            Text("Loading...")
                        }
                    }

                    is UiState.Success -> {
                        state.value?.sessionId?.let {
                            Column(
                                horizontalAlignment = Alignment.CenterHorizontally, // Center items horizontally
                                verticalArrangement = Arrangement.Center // Center items vertically
                            ) {
                                CircularProgressIndicator()
                                Spacer(modifier = Modifier.height(16.dp))
                                Text("Loading... $it")
                            }
                        }
                    }

                    is UiState.Failure -> {
                        Log.d("FaceLivenessDetector", state.exception.toString())
                    }
                }

            }
        }
    }
}

LivenessViewModel.kt

class LivenessViewModel(private val fetchSessionDataUseCase: FetchSessionDataUseCase) :
    ViewModel() {

    private val _uiState = MutableStateFlow<UiState<LivenessModel>>(UiState.Loading)
    val uiState: StateFlow<UiState<LivenessModel>> get() = _uiState

    init {
        fetchSessionData()
    }

    private fun fetchSessionData() {
        val request = LivenessRequest(
            kmsKeyId = null, // Optional
            auditImagesLimit = 3, // Optional
            clientRequestToken = generateRandomString(16) // Required
        )
        viewModelScope.launch {
            fetchSessionDataUseCase.invoke(request).collectLatest { apiResult ->
                when (apiResult) {
                    is ApiResult.Success -> {
                        _uiState.value = UiState.Success(apiResult.value)
                    }

                    is ApiResult.Failure -> {
                        _uiState.value = UiState.Failure(
                            apiResult.errorType ?: ApiException.UnknownError()
                        )
                    }

                    is ApiResult.Loading<*> -> {
                        _uiState.value = UiState.Loading // Emit loading state
                    }
                }
            }
        }

    }
}

FetchSessionDataUseCase.kt

class FetchSessionDataUseCase(private val repository: LivenessRepository) {
    operator fun invoke(request: LivenessRequest): Flow<ApiResult<LivenessModel?>> {
        return flow {
            try {
                emit(Loading<LivenessModel>())
                when (val result = repository.getSessionData(request)) {
                    is Failure -> {
                        emit(Failure(ApiException.UnknownError()))
                    }

                    is Success -> {
                        emit(result.mapSuccess {
                            Success(value)
                        })
                    }

                    else -> {
                        emit(Failure(ApiException.UnknownError()))
                    }
                }

            } catch (e: HttpException) {
                emit(
                    Failure(
                        ApiException.NetworkError(
                            e.localizedMessage ?: "An unexpected error occurred."
                        )
                    )
                )
            } catch (e: IOException) {
                emit(Failure(ApiException.NetworkError("Couldn't reach server. Check your internet connection.")))
            }
        }.catch { e ->
            emit(Failure(ApiException.UnknownError("An error occurred: ${e.localizedMessage ?: "Unknown error"}")))
        }
    }
}

LivenessRepository.kt

interface LivenessRepository {
    suspend fun getSessionData(request: LivenessRequest): ApiResult<LivenessModel?>
}

LivenessRepositoryImpl.kt

class LivenessRepositoryImpl(
    private val dataSource: LivenessRemoteDataSourceImpl
) : LivenessRepository {

    override suspend fun getSessionData(request: LivenessRequest): ApiResult<LivenessModel?> {
        val apiResult = performRequest<LivenessResponse, LivenessModel>(
            request = {
                dataSource.getSessionData(
                    request
                )
            },
            transform = { apiResponse ->
                apiResponse.toDomainModel()
            },
        )
        return apiResult
    }

}

本文标签: androidHow to Prevent Repeated UiStateFailure CallsStack Overflow