如何在Interactor/UseCase中获取CoroutineScope

asc*_*sco 2 android coroutine kotlin 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>

}
Run Code Online (Sandbox Code Playgroud)

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

不鼓励使用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())
    }
Run Code Online (Sandbox Code Playgroud)

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

Kar*_*lon 5

不要将其传递scope给您,UseCase因为您会崩溃Clean Architecture。相反,只需将您的功能保留为Suspend. 如果您希望函数成为concurrent,只需将它们包装在async块中即可。

UseCase在您的中使用ViewModel并使用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))
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

正如所提到的async需要一个范围,一个好的解决方案是创建一个简单的,CoroutineScopeUseCase工作是确定这个将async同时运行这两个任务的单个协程的范围。

val customScope = CoroutineScope(Dispatchers.Main)

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