简体   繁体   中英

Get a single object from a Room query in a background thread and use it in the view. Android/Kotlin/MVVM

In my current solution to this I use coroutines and return a MutableLiveData in the ViewModel that the view.kt class observes.

DAO function:

@Query(
    "SELECT workout_name FROM workout_table " +
        "WHERE id = :workoutId"
)
suspend fun getWorkoutName(workoutId: Long): String

Repository function:

@WorkerThread
suspend fun getWorkoutName(workoutId: Long): String {
    return database.workoutDao().getWorkoutName(workoutId)
}

ViewModel function:

fun getWorkoutName(workoutId: Long): MutableLiveData<String> {
    val workoutName = MutableLiveData<String>()
    CoroutineScope(Dispatchers.IO).launch {
        workoutName.postValue(repository.getWorkoutName(workoutId))
    }
    return workoutName
}

View.kt function:

private fun setUpAppBar() {
    binding?.apply {
        viewModel.getWorkoutName(currentWorkoutId!!).observe(viewLifecycleOwner) {
            editWorkoutTopAppbar.title = it
        }
    }
}

This works but I think there's a better way of doing this. My main problem is in the ViewModel because I'm making a MutableLiveData variable, returning it to the view and giving it a value when ready.

Two other ways I've seen is returning LiveData from the DAO and repository functions and having the view observe it, but this way I think the database query is not done in a background thread. Another way is having the view.kt file launch the coroutine but I believe the view is not supposed to launch coroutines.

The ViewModel using a coroutine when repository returns LiveData:

fun getWorkoutName(workoutId: Long): LiveData<String> {
    CoroutineScope(Dispatchers.IO).launch {
        return repository.getWorkoutName(workoutId)
    }
}

The above code gives me an error because I'm returning the result from inside a coroutine.

Please let me know a solution, or maybe if I'm approaching this problem the wrong way.

First, it's better to use viewModelScope instead of creating new coroutines scope since viewModelScope cancels itself if the veiwModel gets destroyed. secondly, you need to expose a LiveData to fragment or activity instead of MutableLiveData because LiveData can not be manipulated from outside.

so your viewModel should look something like this:


private val _workoutName = MutableLiveData<String>()
val workoutName : LiveData<String>() get() = _workoutName


fun getWorkoutName(workoutId: Long) {
    viewModelScope.launch {
        _workoutName.postValue(repository.getWorkoutName(workoutId))
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM