将 viewModelScope 与 LiveData 一起使用时出现问题
Problem in using viewModelScope with LiveData
我在 ViewModel
中使用 viewModelScope
调用存储库中的挂起函数,如下所示:
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
}
}
存储库
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
}
}
我正在观察 Fragment 中的 LiveData,如下所示:
viewModel.onImageCompressed(compressedImage)?.observe(this, Observer { result ->
when (result) {
is Result.Loading -> {
loader.makeVisible()
}
is Result.Success<*> -> {
// Process result
}
}
})
问题是我没有从 LiveData 获得任何价值。如果我不使用 viewModelScope.launch {}
如下所示,那么一切正常。
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
}
}
我不知道我错过了什么。任何帮助将不胜感激。
此代码:
viewModelScope.launch {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
returns 立即,所以当您第一次调用 onImageCompressed()
方法时,您 return null
为 deepFilterLiveData
。因为在您的 UI 中,您在 onImageCompressed()
的 null
return 值上使用了 ?.
,因此不会达到 when
子句。没有协程的代码可以工作,因为在那种情况下你有顺序代码,你的 ViewModel 等待存储库调用。
要解决此问题,您可以保留 ViewModel-UI 交互的 LiveData 和 return 直接来自存储库方法的值:
class DeepFilterRepository {
suspend fun applyFilter(compressedImage: File) = withContext(Dispatchers.IO) {
Result.Success("Done")
}
}
和视图模型:
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)
}
}
}
我在 ViewModel
中使用 viewModelScope
调用存储库中的挂起函数,如下所示:
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
}
}
存储库
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
}
}
我正在观察 Fragment 中的 LiveData,如下所示:
viewModel.onImageCompressed(compressedImage)?.observe(this, Observer { result ->
when (result) {
is Result.Loading -> {
loader.makeVisible()
}
is Result.Success<*> -> {
// Process result
}
}
})
问题是我没有从 LiveData 获得任何价值。如果我不使用 viewModelScope.launch {}
如下所示,那么一切正常。
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
}
}
我不知道我错过了什么。任何帮助将不胜感激。
此代码:
viewModelScope.launch {
deepFilterLiveData = repo.applyFilter(compressedImage)
}
returns 立即,所以当您第一次调用 onImageCompressed()
方法时,您 return null
为 deepFilterLiveData
。因为在您的 UI 中,您在 onImageCompressed()
的 null
return 值上使用了 ?.
,因此不会达到 when
子句。没有协程的代码可以工作,因为在那种情况下你有顺序代码,你的 ViewModel 等待存储库调用。
要解决此问题,您可以保留 ViewModel-UI 交互的 LiveData 和 return 直接来自存储库方法的值:
class DeepFilterRepository {
suspend fun applyFilter(compressedImage: File) = withContext(Dispatchers.IO) {
Result.Success("Done")
}
}
和视图模型:
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)
}
}
}