Android 的干净架构 (MVVM) 中的视图、视图模型、用例、存储库、服务和 dao 之间的通信

问题描述 投票:0回答:1

使用

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)

但是,我有几个问题或疑问:

  1. 是否会导致内存泄漏?
  2. 我是否以正确的方式使用
    flow
  3. 我应该在
    take(1)
    中的
    first()
    上使用
    repository.getItems()
    还是
    useCase
  4. 当数据库中没有数据时,我们返回默认值。当用户打开关联的屏幕或执行
    pull To Refresh,
    操作时,我们将进行 API 调用并将数据保存到数据库中。我相信更新将按以下顺序进行:从数据库到
    repository,
    useCase,
    ViewModel,
    fragment.
    但我怀疑
    useCase
    ,因为它取消了范围并关闭了通道。我怀疑它不会收到任何更新,直到我们再次调用
    useCase.getItems
    再次,开始从
    repository.
    收集流量,是吗?如果是,我们如何设置整个通信,以便每当数据库发生更改时
    fragment
    始终收到更新,而无需在进行 API 调用后再次调用
    useCase.getItems
    函数?
  5. 对于这样的要求,我应该使用
    suspendCancellableCoroutine
    而不是
    callbackFlow
    吗?
android android-livedata clean-architecture kotlin-flow android-mvvm
1个回答
2
投票

我建议您通过多种方式改进代码:

  1. 我不知道你的数据库,因此我无法对此发表评论。 但是,我会尝试从数据库获取流量,而不是实时数据。 (Livedata 是为了与用户界面相关的目的而制作的,而不是数据库的东西)

  2. 更喜欢变量而不是 getFunctions() 这样,如果两方尝试访问相同的项目,他们实际上使用的是相同的。

    // your repository will then be sth.like this:
    val items: Flow<List<Item>> = ...
    
  3. 您的用例太复杂了。尝试这样的事情:

    val items: Flow<List<Item>> = repository.items
        .map { list -> 
            val filteredList = list
                .filter { it.isValid }
                .map { it.toUiModel() }
    
            if(filteredList.isEmpty()) defaultList else filteredList
        }
    
  4. 您的

    viewModel
    可能会立即将数据转发到用户界面: val items: Flow = usecase.items

  5. 在您的片段中,您可以收集此处指定的项目:https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda。这完成了生命周期和暂停流收集等的所有处理。

  6. 命名:

    Items
    真的应该是复数吗?

  7. 在我们公司,我们在存储库中进行 dbModel -> domainModel 的映射。您在用例中这样做有什么原因吗?

  8. 关于“如何更新”数据: Ui -> viewModel -> useCase -> 存储库。 Repository将调用api,并将新数据保存到数据库中。 然后,更新的数据应该通过我们上面设置的项目链自动转发到 ui。

  9. 以防万一您刚刚开始使用应用程序:放弃 Jetpack Compose 的片段,因为那是 Android ui 的未来。

© www.soinside.com 2019 - 2024. All rights reserved.