【问题标题】:How to get CoroutineScope in Interactor / UseCase如何在交互/用例中获取 CoroutineScope
【发布时间】:2020-04-23 20:51:42
【问题描述】:

考虑这个用例:

class GetPhotosUseCase(
    private val photosRepository: IPhotosRepository,
    private val favoritesRepository: IFavoritesRepository
) : IGetPhotosUseCase {

    override suspend fun getPhotos(): List<Photo> {
        val photos = photosRepository.getPhotos()
        val favoriteIds = favoritesRepository.getFavoriteIds()
        return photos.map {
            it.copy(isFavorite = favoriteIds.contains(it.id))
        }
    }

}

interface IPhotosRepository {

    suspend fun getPhotos(): List<Photo>

}

interface IFavoritesRepository {

    suspend fun getFavoriteIds(): List<Int>

}

我从 2 个不同的来源获取数据并将它们组合起来。现在,这是连续运行的。 当我想并行运行photosRepository.getPhotos()favoritesRepository.getFavoriteIds() 以节省执行时间时,我的幼稚做法是:

override suspend fun getPhotos(): List<Photo> {
    val photosDeferred = GlobalScope.async { photosRepository.getPhotos() }
    val favoriteIdsDeferred = GlobalScope.async { favoritesRepository.getFavoriteIds() }
    return applyFavoritesToPhotos(photosDeferred.await(), favoriteIdsDeferred.await())
}

private fun applyFavoritesToPhotos(photos: List<Photo>, favoriteIds: List<Int>) = photos.map {
    it.copy(isFavorite = favoriteIds.contains(it.id))
}

不鼓励使用GlobalScope,因为调用者的生命周期结束时不会取消作业。

由于我的用例不知道调用者的生命周期,它应该使用哪个范围? 将范围传递给用例是否是一个令人满意的解决方案,例如:

   override suspend fun getPhotos(scope: CoroutineScope): List<Photo> {
        val photosDeferred = scope.async { photosRepository.getPhotos() }
        val favoriteIdsDeferred = scope.async { favoritesRepository.getFavoriteIds() }
        return applyFavoritesToPhotos(photosDeferred.await(), favoriteIdsDeferred.await())
    }

或者这里的理想解决方案是什么?用例是否应该返回一个Deferred 并让调用者await 它?

【问题讨论】:

标签: android kotlin coroutine kotlin-coroutines


【解决方案1】:

不要将 scope 传递给您的 UseCase,因为您会破坏 Clean Architecture。相反,只需将您的函数保留为 Suspend。如果您希望函数为 concurrent,只需将它们包装在 async 块中。

在您的 ViewModel 中使用 UseCase 并使用 ViewModelScope 调用 suspend 函数。

override suspend fun getPhotos(): List<Photo> {
    val photos = async { photosRepository.getPhotos() }
    val favoriteIds = async { favoritesRepository.getFavoriteIds() }

    return photos.await().map {
        it.copy(isFavorite = favoriteIds.await().contains(it.id))
    }
}

编辑

如前所述,async 需要一个作用域,一个好的解决方案是在 UseCase 中创建一个简单的 CoroutineScope他的工作是确定将同时运行这两个 async 任务的单个协程的范围。

val customScope = CoroutineScope(Dispatchers.Main)

override suspend fun getPhotos(): List<Photo> {
    customScope.launch {
        val photos = async { photosRepository.getPhotos() }
        val favoriteIds = async { favoritesRepository.getFavoriteIds()}
    }
    .
    .
    .
}

【讨论】:

  • 没有 CoroutineScope 就无法启动异步(在早期版本中可能有所不同)。
  • 已编辑答案,请参考。
猜你喜欢
  • 2012-01-29
  • 2021-06-16
  • 2019-09-22
  • 2022-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
相关资源
最近更新 更多