使用 kotlin co 例程从单个函数多次返回

问题描述 投票:0回答:3

在此 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
    }}
android kotlin repository kotlin-coroutines clean-architecture
3个回答
6
投票

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)
}

0
投票

您可以使用任何异步和基于事件的库来完成此操作。

您可以使用 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 已经提到的流程

您也可以使用实时数据。


0
投票

您可以使用协程中的

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
注释标记为实验性。

© www.soinside.com 2019 - 2024. All rights reserved.