android - 如何在Interactor/UseCase中获取CoroutineScope

标签 android kotlin coroutine kotlin-coroutines

考虑这个用例:

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>

}

我从两个不同的来源获取数据并将它们组合起来。现在,这是连续运行的。 当我想并行运行 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它?

最佳答案

不要将范围传递给您的UseCase,因为您会破坏干净的架构。相反,只需将您的函数保持为挂起即可。如果您希望函数并发,只需将它们包装在async block 中。

ViewModel 中使用 UseCase 并使用 ViewModelScope 调用挂起函数。

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 其工作是确定将同时运行这两个 async 任务的单个协程的范围。

val customScope = CoroutineScope(Dispatchers.Main)

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

关于android - 如何在Interactor/UseCase中获取CoroutineScope,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61396762/

相关文章:

python - `asyncio.wait([asyncio.sleep(5)])` 和 `asyncio.sleep(5)` 之间的区别

android - 宽度 :100% in CSS not rendering well in Android 4. 4

android - 向上导航按钮在首选项 Activity 中不起作用

Kotlin 优化使用过滤器获取前 n 个元素

kotlin - kotlin "remove"的可为空参数

kotlin - 在 TextInputLayout 中设置文本时默认显示 EndIcon

kotlin - 修改延迟结果

android - 如何使用凭据从 Android 调用 .NET Web 服务?

Android 启动画面计时器无法正常工作

c# - 使用 await 关键字实现游戏脚本的协程