在此 Repository 类中,只有一个称为 getMovies 的公共函数,直接从 useCase 类调用。
现在的问题是我希望这个函数从单个调用中多次返回 movieData ,首先我想从数据库本地获取数据并返回它,然后从远程获取数据并执行一些逻辑&在逻辑结束后我也想返回最终值我怎样才能用协程做到这一点。
整个解决方案在 getMovies func 的返回行中。
注意:这可以使用 liveData 或 rx 来完成,但我不想使用它们中的任何一个,因为通过所有这些以 viewModel 结尾的层传递 livedata 并不是一个好主意。
这是 repo 类:
@Singleton
class MovieRepository @Inject constructor(
private val movieDao: MovieDao,
private val movieApi: MovieApi,
private val trailerApi: TrailerApi) : BaseRepository() {
suspend fun getMovies(): ArrayList<Movie> {
val localData = fetchMoviesLocal()
val remoteData = fetchMoviesRemote()
val syncedData = storeMoviesLocal(remoteData)
return localData then syncedData
}
private fun fetchMoviesLocal(): ArrayList<Movie> = movieDao.fetchAllMovies()
private suspend fun fetchMoviesRemote(): ArrayList<Movie>? {
val data = safeApiCall({ movieApi.getMostPopular(AppConstants.API_KEY) },
"fetching movies")
return if (data != null) data.results as ArrayList<Movie> else null
}
private fun storeMoviesLocal(results: List<Movie>?): ArrayList<Movie>? {
return if (!results.isNullOrEmpty()) syncFavWithDb(results) else null
}
private fun syncFavWithDb(movies: List<Movie>): ArrayList<Movie> {
val tempList = ArrayList<Movie>()
movies.forEach { movie -> movie.isFav = if (isMovieLiked(movie.id)) 1 else 0; tempList.add(movie) }
movieDao.insertAll(tempList)
return tempList
}}
Kotlin 1.3 引入了自己的冷异步流的稳定版本 - Flows:
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
fun getMovies(): Flow<List<Movie>> = flow {
val localData = fetchMoviesLocal()
emit(localData)
val remoteData = fetchMoviesRemote()
val syncedData = storeMoviesLocal(remoteData)
emit(syncedData)
}
您可以使用任何异步和基于事件的库来完成此操作。
您可以使用 rx-java 发布主题:subjects rx-java
val subject = PublishSubject();
fun getMovies(): Flow<List<Movie>> = flow {
val localData = fetchMoviesLocal()
emit(localData)
val remoteData = fetchMoviesRemote()
val syncedData = storeMoviesLocal(remoteData)
subject.onNext(syncedData)
}
您可以在任何需要数据的地方订阅该主题
您可以使用 @jsamol 已经提到的流程
您也可以使用实时数据。
您可以使用协程中的
produce { }
。它类似于 Dart 中 yield
关键字的工作原理。它是一个阻塞函数,因此您可以在循环中使用:
fun main() {
runBlocking {
val fibonacci = emitIntegers()
var i: Int
while (fibonacci.receive().also { i = it } < 10) {
println("Received $i")
}
// Cancel children coroutines to prevent infinite loop
coroutineContext.cancelChildren()
}
}
suspend fun CoroutineScope.emitIntegers(): ReceiveChannel<Int> = produce {
var i = 0
while (true) {
println("Emitting $i")
send(i)
i++
delay(1000)
}
}
它将产生以下输出:
Emitting 0
Received 0
Emitting 1
Received 1
...
Emitting 9
Received 9
Emitting 10
此功能的最佳用例是当您使用
sealed class
来定义阻塞函数的当前状态时:
fun main() {
runBlocking {
val file = copyFile()
for (result in file) { // similar to while(true)
when (result) {
is Result.InProgress -> println("Progress: ${result.progress}%")
is Result.Success -> println("File copied: ${result.file}")
is Result.Error -> println("Error: ${result.message}")
}
}
coroutineContext.cancelChildren()
}
}
suspend fun CoroutineScope.copyFile(): ReceiveChannel<Result> = produce {
var i = 0
while (i < 10) {
i++
delay(1000)
send(Result.InProgress(i * 10))
}
send(Result.Success(File("/mnt/sdcard/Download/new_file.txt")))
}
sealed class Result {
class InProgress(val progress: Int) : Result()
class Success(val file: File) : Result()
class Error(val message: String) : Result()
}
输出:
Progress: 10%
Progress: 20%
...
Progress: 90%
Progress: 100%
File copied: /mnt/sdcard/Download/new_file.txt
注意:
produce { }
仍被 @ExperimentalCoroutinesApi
注释标记为实验性。