I am using viewModelScope
in the ViewModel
which calls a suspend function in the repository as shown below:
ViewModel
class DeepFilterViewModel(val repo: DeepFilterRepository) : ViewModel() {
var deepFilterLiveData: LiveData<Result>? = null
fun onImageCompressed(compressedImage: File): LiveData<Result>? {
if (deepFilterLiveData == null) {
viewModelScope.launch {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
}
return deepFilterLiveData
}
}
Repository
class DeepFilterRepository {
suspend fun applyFilter(compressedImage: File): LiveData<Result> {
val mutableLiveData = MutableLiveData<Result>()
mutableLiveData.value = Result.Loading
withContext(Dispatchers.IO) {
mutableLiveData.value = Result.Success("Done")
}
return mutableLiveData
}
}
I am observing the LiveData from the Fragment as shown below:
viewModel.onImageCompressed(compressedImage)?.observe(this, Observer { result ->
when (result) {
is Result.Loading -> {
loader.makeVisible()
}
is Result.Success<*> -> {
// Process result
}
}
})
The problem is I am getting no value from the LiveData. If I don't use viewModelScope.launch {}
as shown below, then everything works fine.
class DeepFilterViewModel(val repo: DeepFilterRepository) : ViewModel() {
var deepFilterLiveData: LiveData<Result>? = null
fun onImageCompressed(compressedImage: File): LiveData<Result>? {
if (deepFilterLiveData == null) {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
return deepFilterLiveData
}
}
I don't know what I am missing. Any help will be appreciated.
Flow: Simple things are harder and complex things are easier. LiveData did one thing and it did it well: it exposed data while caching the latest value and understanding Android's lifecycles. Later we learned that it could also start coroutines and create complex transformations, but this was a bit more involved.
LiveData is lifecycle-aware, following the lifecycle of entities such as activities and fragments. Use LiveData to communicate between these lifecycle owners and other objects with a different lifespan, such as ViewModel objects.
This code:
viewModelScope.launch {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
returns immediately so when you first invoke the onImageCompressed()
method you return null
as deepFilterLiveData
. Because in your UI you use ?.
on the null
return value of onImageCompressed()
the when
clause will not be reached. The code without the coroutine works because in that case you have sequential code, your ViewModel awaits for the repository call.
To solve this you could keep the LiveData for the ViewModel-UI interaction and return the values directly from the repository method:
class DeepFilterRepository {
suspend fun applyFilter(compressedImage: File) = withContext(Dispatchers.IO) {
Result.Success("Done")
}
}
And the ViewModel:
class DeepFilterViewModel(val repo: DeepFilterRepository) : ViewModel() {
private val _backingLiveData = MutableLiveData<Result>()
val deepFilterLiveData: LiveData<Result>
get() = _backingLiveData
fun onImageCompressed(compressedImage: File) {
// you could also set Loading as the initial state for _backingLiveData.value
_backingLiveData.value = Result.Loading
viewModelScope.launch {
_backingLiveData.value = repo.applyFilter(compressedImage)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With