我正在Kotlin应用程序中实现MVVM,我试图找到正确的方法来处理应该从我的回购中获取数据的按钮单击。
这是使用LiveData构建我的应用程序的方式:
片段(观察LiveData)-> ViewModel(包含LiveData)->存储库(返回LiveData)->网络(返回数据)。
如您所见,我的存储库方法全部返回LiveData,而我的网络方法返回Data,这意味着实际的异步api调用是在存储库级别(使用协程)进行的,然后将响应数据包装为实时数据。数据对象并传递回视图模型。
我正在观察片段中的数据,我想要的最终结果是单击片段中的一个按钮,该按钮将触发viewmodel从网络中获取数据。
这是我的片段的样子:
// Fragment.kt
// onActivityCreated()
viewModel.allTestObjects.observe(this, Observer { allTestObjects ->
my_text_view.text = allTestObjects?.toString()
my_button.setOnClickListener {
viewModel.getAllTestObjects()
}
现在是ViewModel:
// TestObjectViewModel.kt
val allTestObjects: MutableLiveData<Resource<List<TestObject>>> = MutableLiveData()
fun getAllTestObjects() {
allTestObjects.value = testObjectRepository.getAllTestObjects().value
}
我的存储库:
// TestObjectRepository.kt
fun getAllTestObjects(): LiveData<Resource<List<TestObject>>> {
val result = MutableLiveData<Resource<List<TestObject>>>()
scope.launch {
val response = testObjectApi.getAllTestObjects()
if (response.isSuccessful) {
response.body()?.let { allTestObjects ->
result.postValue(Resource.success(allTestObjects))
} ?: result.postValue(Resource.empty())
} else {
result.postValue(Resource.error(response.message(), null))
}
}
return result
}
请注意,协程范围scope
是从ViewModel向下传递的viewModelScope
最后是翻新Api:
// TestObjectApi.kt
@GET(".")
suspend fun getAllTestObjects() : Response<List<TestObject>>
这里的问题特别是这一行:
allTestObjects.value = testObjectRepository.getAllTestObjects().value
的行为不符合我的预期。
存储库正在创建一个实时数据并同步返回它,但是在调用postValue
时,或者至少不是返回到viewModel的实时数据时,此实时数据不会得到更新。换句话说,即使在存储库中发布值之后,allTestObjects.value
也从未改变。
我不确定自己在做什么错以及如何解决此问题。
我看了很多示例,其中将视图模型内部的实时数据分配给该变量,并在onCreate而不是Click侦听器中进行了观察。如果您想在onCreate中获取数据,这会很好用,但是在我的情况下,我只想在单击按钮时获取并更新数据,但是当然要在onCreate中观察此数据。
我还对Transformations
和MediatorLiveData
进行了试验,我能够得到一个可行的解决方案,并给了我预期的行为,但它没有我期望的那么干净。
我发现的另一个“肮脏”解决方案是,视图模型中的livedata变量与实际数据不同:
我的视图模型现在看起来像这样:
val fetchAllObjects = MutableLiveData<Boolean>()
val allTestObjects: LiveData<Resource<List<TestObject>>> = Transformations.switchMap(fetchAllObjects) { shouldFetch ->
if(shouldFetch)
testObjectRepository.getAllTestObjects()
else
MutableLiveData()
和我的片段是这样的:
my_button.setOnClickListener {
viewModel.fetchAllTestObjects.value = true
}
这很好用,但是我不喜欢必须设置一个值而不是打电话的事实,即。我将fetchAllObjects
的布尔值设置为当我觉得数据看起来更像是viewModel.getAllTestObjects()
这样的直接方法调用时获取数据,更不用说为每个需要的调用添加额外的标志了这会污染视图模型。
也许我只是偏执狂,或者我想念一些明显的东西,请告诉我您对此的想法。
谢谢!