使用
monarchy realm
数据库,dao
给出 liveData.
A repository
将 liveData
提供给 useCase
作为 flow
在 .asFlow
上使用 liveData.
useCase
需要到在进行一些 flow
ViewModel
和其他验证后,将 filter,
返回到 map,
。 ViewModel
从 flow
收集 useCase
并将更新发布到本地 mutableLiveData.
A view
观察 liveData
的 mutableLiveData.
形式 View
接收更新!
//fragment.kt
fun initObserver() {
viewModel.itemsLiveData.observe(viewLifeCycleOwner) { list ->
onUpdateItems(list)
}
}
//viewModel.kt
private val _itemsLiveData = MutableLiveData<List<Items>>()
val itemsLiveData = _itemsLiveData
init {
getItems()
}
fun getItems() {
viewModelScope.launch(Dispatchers.IO) {
useCase.getItems().collectLatest { items ->
_itemsLiveData.postValue(items)
}
}
}
//usecase.kt (callbackFlow or suspendCancellableCoroutine?)
suspend fun getItems(): callbackFlow<List<Items>> {
repository.getItems().collectLatest { dbItems ->
val validList = dbItems.filter { it.isValid() }.map { it.toUiModel() }
if (validList.isNotEmpty()) {
trySendBlocking(validList)
} else {
trySendBlocking(defaultList)
}
}
//Cancel this coroutine scope and it's children.
awaitClose {
cancel()
}
//Close this channel. No more sending. Sending operation is closed.
channel.close()
}
suspend fun apiCall() {
val result = api.getItems()
if (result.isSuccessful) {
val items = result.body()?.items
if (items != null && items.isNotEmpty()) {
repository.saveItems(items)
}
}
}
//repository.kt
suspend fun getItems() = withContext(Dispatchers.Main) { dao.getItemsLiveData().asFlow() }
suspend fun saveItems(items: Items) = withContext(Dispatchers.Main) { dao.saveItems(items) }
//dao.kt
fun getItemsLiveData() = LiveData<Items>()
fun saveItems(items: Items)
但是,我有几个问题或疑问:
flow
?take(1)
中的first()
上使用repository.getItems()
还是useCase
?pull To Refresh,
操作时,我们将进行 API 调用并将数据保存到数据库中。我相信更新将按以下顺序进行:从数据库到repository,
到useCase,
到ViewModel,
到fragment.
但我怀疑useCase
,因为它取消了范围并关闭了通道。我怀疑它不会收到任何更新,直到我们再次调用 useCase.getItems
再次,开始从 repository.
收集流量,是吗?如果是,我们如何设置整个通信,以便每当数据库发生更改时fragment
始终收到更新,而无需在进行 API 调用后再次调用 useCase.getItems
函数?suspendCancellableCoroutine
而不是 callbackFlow
吗?我建议您通过多种方式改进代码:
我不知道你的数据库,因此我无法对此发表评论。 但是,我会尝试从数据库获取流量,而不是实时数据。 (Livedata 是为了与用户界面相关的目的而制作的,而不是数据库的东西)
更喜欢变量而不是 getFunctions() 这样,如果两方尝试访问相同的项目,他们实际上使用的是相同的。
// your repository will then be sth.like this:
val items: Flow<List<Item>> = ...
您的用例太复杂了。尝试这样的事情:
val items: Flow<List<Item>> = repository.items
.map { list ->
val filteredList = list
.filter { it.isValid }
.map { it.toUiModel() }
if(filteredList.isEmpty()) defaultList else filteredList
}
您的
viewModel
可能会立即将数据转发到用户界面:
val items: Flow在您的片段中,您可以收集此处指定的项目:https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda。这完成了生命周期和暂停流收集等的所有处理。
命名:
Items
真的应该是复数吗?
在我们公司,我们在存储库中进行 dbModel -> domainModel 的映射。您在用例中这样做有什么原因吗?
关于“如何更新”数据: Ui -> viewModel -> useCase -> 存储库。 Repository将调用api,并将新数据保存到数据库中。 然后,更新的数据应该通过我们上面设置的项目链自动转发到 ui。
以防万一您刚刚开始使用应用程序:放弃 Jetpack Compose 的片段,因为那是 Android ui 的未来。