Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
163 views
in Technique[技术] by (71.8m points)

Kotlin not getting called from view model

I am trying call

override suspend fun getLoginResponse(loginRequest: LoginRequest) = flow {
    emit(ApiResult.Loading)
    networkCall {
        loginService.postLoginResponse(loginRequest)
    }.let { apiResult->
        apiResult.isSuccessAndNotNull().letOnTrueOnSuspend {
            (apiResult.getResult() as? LoginResponse)?.let {
                emit(ApiResult.Success(it))
                Timber.d(it.toString())
            } ?: run {  emit(ApiResult.Error(TypeCastException("unknown error.")))
                Timber.d(TypeCastException("unknown error."))}
        }
    }
}.flowOn(Dispatchers.IO)

from my viewModel like this :

private fun loginResponse(email: String, password: String, device: String){
    viewModelScope.launch {
        try {
            var loginRequest = LoginRequest(email, password, device)
            loginResponseFromServer = loginRepository.getLoginResponse(loginRequest)
                .asLiveData(viewModelScope.coroutineContext+Dispatchers.Default)
            Timber.d(loginResponseFromServer.toString())
        }
        catch (e: NetworkErrorException){
            validationError.value = "Network communication error!"
        }
    }
}

When I debug or run the code getLoginResponse not even calling. Is there anything I am missing?

question from:https://stackoverflow.com/questions/65873072/kotlin-not-getting-called-from-view-model

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

First of all, getLoginResponse doesn't need to be a suspend function since it just returns a cold Flow. If you remove the suspend modifier, you won't need a coroutine to call it or convert it to LiveData.

Second, a LiveData that is built with .asLiveData() doesn't begin to collect the Flow (remains cold) until it first becomes active. This is in the docs for the function. It becomes active when it receives its first observer, but your code has not begun to observe it, which is why the code in your flow block is never called.

You also don't need to specify a different dispatcher for your LiveData. It doesn't matter which dispatcher you're collecting in since collecting it isn't blocking code.

However, LiveData isn't something that should be collected within a ViewModel. It's for UI to interact. The LiveData should be observed from the Fragment.

You need to move your catching of the network exception into your flow builder. The exception will not be thrown at the time of creating the Flow or LiveData, but rather at the time the request is being made (in the Flow's execution).

I'm not sure exactly how to rewrite your flow builder to properly catch because it has functions I haven't seen. Just a tip, but chaining together lots of scope functions into one statement makes code hard to read and reason about.

So to do this as LiveData, you can change your code as follows:

private fun loginResponse(email: String, password: String, device: String): LiveData<LoginResponse> {
    val loginRequest = LoginRequest(email, password, device)
    return loginRepository.getLoginResponse(loginRequest)
        .asLiveData()
}

And then observe it in your Fragment.

However

LiveData and Flow don't really fit this use case, because you want to make a single request and get a single response. Your repository should just expose a suspend function that returns the response. Then your ViewModel can have a suspend function that just passes through the response by calling the repository's suspend function.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...