【问题标题】:Which is better to put the Coroutine call in the Repository or the ViewModel?将 Coroutine 调用放在 Repository 或 ViewModel 中哪个更好?
【发布时间】:2019-08-04 08:41:40
【问题描述】:

只是想知道其他人的意见我有两种方法可以做某事并且很好奇哪种方法更好(希望你为什么这么认为)

我有 2 个文件 WordRepository 和 WordViewModel。我可以在 Repo 或 ViewModel 中执行协程,这两种方式都可以工作,但希望有人能告诉我为什么我会在其中一种中执行协程,反之亦然。

版本 A.(协程在 Repo 中的位置)

WordRepo:

class WordRepository(private val wordDao: WordDao): WordRepo {

    @WorkerThread
    override suspend fun deleteAllLogsOlderThan(XDays: Int): Int = withContext(IO) {
        return@withContext wordDao.deleteAll()
    }

}

WordViewModel:

class WordViewModel(private val wordRepository: WordRepo) : ViewModel() {

    fun deleteAllLogsOlderThanA(XDays:Int): Int = runBlocking {
        wordRepository.deleteAllLogsOlderThan(XDays)
    }

}

版本 B.(协程在 ViewModel 中的位置)

Word Repo:
class WordRepository(private val wordDao: WordDao): WordRepo {

    @WorkerThread
    override suspend fun deleteAllLogsOlderThan(XDays: Int): Int = wordDao.deleteAll()

}

WordViewModel:
class WordViewModel(private val wordRepository: WordRepo) : ViewModel() {

    fun deleteAllLogsOlderThanA(XDays:Int): Int = runBlocking {
            withContext(IO) {
                wordRepository.deleteAllLogsOlderThan(XDays)
            }
        }

}

【问题讨论】:

  • runBlocking 阻塞当前线程,确定要使用吗?
  • 我还能如何调用 withContext 并获得响应不能使用启动,因为它返回工作?
  • launch 用于启动非阻塞协程。如果您不希望您的函数返回 Job,您可以创建一个包装 launch 调用并返回 Unit 的函数。
  • 使用协程你不能在不阻塞线程的情况下返回值。如果您在 ViewModel 中有 MutableLiveData 对象,您可以使用它来更新 UI:launch { liveData.value = wordRepository.deleteAllLogsOlderThan(XDays)}
  • 我更喜欢使用本地范围在ViewModelPresenter 中启动协程。这样在WordViewModeldeleteAllLogsOlderThanA函数中启动了一个协程

标签: android mvvm kotlin android-room coroutine


【解决方案1】:

老实说,我认为任何一种方式都很好,但如果我必须选择,我更喜欢将线程相关的代码保留在存储库层中。 (示例中为版本 A

我的理由:

(A) 视图模型不应该假设存储库在内部是如何工作的。 版本 B 意味着视图模型假定存储库将在调用线程上运行。

(B) 此外,存储库不应依赖其他组件来使用特定线程来调用其方法。存储库本身应该是完整的。

(C) 避免代码重复。让多个视图模型调用一个存储库方法是一种非常常见的情况。 版本 A 在这种情况下获胜,因为您只需要在一个地方,即存储库中使用 Corountine


我建议查看一些 Google 示例,看看他们喜欢在哪里处理 Coroutine 和数据访问线程代码。

  1. Sunflower 在其存储库中使用 Coroutine 代码。这可能对您特别有用,因为它包含了即发即弃类型的请求示例。

  2. GitHubBrowserRepo 不使用 Coroutine,但存储库会引用访问数据时使用的 Executor 实例。

【讨论】:

  • 谢谢 :) 我在考虑版本 A,但我的主要原因是您的 C 版本,所以其他原因非常有帮助。我一定会查看 2 个 google 示例项目。
  • 那我应该在哪里创建协程作业呢?在视图模型或存储库中?我应该什么时候清除以防止内存泄漏?
  • 如果您使用的是Android ViewModel,您可以在viewmodel中创建作业并在ViewModel#onCleared中清除作业
  • 至 (A):版本 B 假定存储库位于运行线程上,但版本 A 假定它位于工作线程上。哪个更糟?
  • @Malachiasz 在版本 A 中,ViewModel 不知道存储库在工作线程上运行。您可以将版本 B 的 ViewModel 复制粘贴到版本 A,它可能仍然有效。反过来可能会失败。
【解决方案2】:

问题是在哪里指定存储库作业应该在 IO 线程池上运行:

  • A) 在存储库中
  • B) 在 ViewModel 中

坦率地说,我不确定哪种方式更好。

B) 在 ViewModel 中包含它意味着在哪个线程上运行的内容更加透明。这也是我主要使用 RxJava 的方式。不利的是,您的 ViewModel 将因线程切换(withContext())而变得混乱,而实际上很明显所有存储库作业都应在后台线程上运行。那么这些额外的信息有用吗?

A) 将它放在存储库中意味着在存储库中的线程切换和 ViewModel 中更简洁的代码方面具有更大的灵活性。从 ViewModel 的角度来看,关于线程的代码将不那么明确。不利的是,所有存储库方法都需要暂停,因此只能在协程中使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    • 2018-11-13
    相关资源
    最近更新 更多