Home > Net >  Observe callback is not being triggered with Kotlin coroutines Flow and LiveData?
Observe callback is not being triggered with Kotlin coroutines Flow and LiveData?

Time:05-19

I'm new to Android development and trying to understand Coroutines and LiveData from various example projects. I have currently setup a function to call my api when the user has input a username and password. However after 1 button press, the app seems to jam and I can't make another api call as if its stuck on a pending process.

This is my first android app made with a mash of ideas so please let me know where I've made mistakes!

Activity:

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        viewModel.userClicked(username, password).observe(this, Observer {
            it?.let { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        })
 }

ViewModel:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    viewModelScope.launch {
        emit(Resource.loading(data = null))
        try {
            userRepository.login(username, password).apply {
                emit(Resource.success(null))
            }
        } catch (exception: Exception) {
            emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
        }
    }
}

Repository:

@WorkerThread
suspend fun login(
    username: String,
    password: String
): Flow<Resource<String?>> {
    return flow {
        emit(Resource.loading(null))
        api.login(LoginRequest(username, password)).apply {
            this.onSuccessSuspend {
                data?.let {
                    prefs.apiToken = it.key
                    emit(Resource.success(null))
                }
            }
        }.onErrorSuspend {
            emit(Resource.error(message(), null))
        }.onExceptionSuspend {
            emit(Resource.error(message(), null))
        }
    }.flowOn(dispatcherIO)
}

API:

suspend fun login(@Body request: LoginRequest): ApiResponse<Auth>

CodePudding user response:

You don't need to launch a coroutine in liveData builder, it is already suspend so you can call suspend functions there:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    emit(Resource.loading(data = null))
    try {
        userRepository.login(username, password).apply {
            emit(Resource.success(null))
        }
    } catch (exception: Exception) {
        emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
    }
}

If you want to use LiveDate with Flow you can convert Flow to LiveData object using asLiveData function:

fun userClicked(username: String, password: String): LiveData<Resource<String?>> {
    return userRepository.login(username, password).asLiveData()
}

But I wouldn't recommend to mix up LiveData and Flow streams in the project. I suggest to use only Flow.

Using only Flow:

// In ViewModel:

fun userClicked(username: String, password: String): Flow<Resource<String?>> {
    return userRepository.login(username, password)
}

// Activity

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        lifecycleScope.launch {
            viewModel.userClicked(username, password).collect { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        }
    }

Remove suspend keyword from the login function in Repository.

lifecycleScope docs.

  • Related