在主线程

时间:2018-06-12 21:03:27

标签: android firebase kotlin google-cloud-firestore coroutine

我正在构建一个基于Android Clean Architecture Kotlin版本(https://github.com/android10/Android-CleanArchitecture-Kotlin)的应用程序。

使用这种架构,每次要调用用例时,都会启动Kotlin协程,并将结果发布到主线程中。这是通过以下代码实现的:

abstract class UseCase<out Type, in Params> where Type : Any {

abstract suspend fun run(params: Params): Either<Failure, Type>

fun execute(onResult: (Either<Failure, Type>) -> Unit, params: Params) {
    val job = async(CommonPool) { run(params) }
    launch(UI) { onResult.invoke(job.await()) }
}

在他的示例架构中,Android10先生使用Retrofit在kotlin couroutine中进行同步api调用。例如:

override fun movies(): Either<Failure, List<Movie>> {
            return when (networkHandler.isConnected) {
                true -> request(service.movies(), { it.map { it.toMovie() } }, emptyList())
                false, null -> Left(NetworkConnection())
            }
        }

private fun <T, R> request(call: Call<T>, transform: (T) -> R, default: T): Either<Failure, R> {
            return try {
                val response = call.execute()
                when (response.isSuccessful) {
                    true -> Right(transform((response.body() ?: default)))
                    false -> Left(ServerError())
                }
            } catch (exception: Throwable) {
                Left(ServerError())
            }
        }

&#39;要么&#39;表示不相交的类型,意味着结果将是失败或您想要的类型T的对象。

他的service.movi​​es()方法实现如此(使用改造)

@GET(MOVIES) fun movies(): Call<List<MovieEntity>>

现在这是我的问题。我正在用Google Cloud Firestore取代改造。我知道目前,Firebase / Firestore是一个全异步库。我想知道是否有人知道一种更优雅的方法来对Firebase进行同步API调用。

我实现了自己的Call版本:

interface Call<T: Any> {
    fun execute(): Response<T>

    data class Response<T>(var isSuccessful: Boolean, var body: T?, var failure: Failure?)
}

我的API调用已在此处实施

override fun movieList(): Call<List<MovieEntity>> = object : Call<List<MovieEntity>> {
        override fun execute(): Call.Response<List<MovieEntity>> {
            return movieListResponse()
        }
    }

    private fun movieListResponse(): Call.Response<List<MovieEntity>> {
        var response: Call.Response<List<MovieEntity>>? = null
        FirebaseFirestore.getInstance().collection(DataConfig.databasePath + MOVIES_PATH).get().addOnCompleteListener { task ->
            response = when {
                !task.isSuccessful -> Call.Response(false, null, Failure.ServerError())
                task.result.isEmpty -> Call.Response(false, null, MovieFailure.ListNotAvailable())
                else -> Call.Response(true, task.result.mapTo(ArrayList()) { MovieEntity.fromSnapshot(it) }, null)
            }
        }
        while (response == null)
            Thread.sleep(50)

        return response as Call.Response<List<MovieEntity>>
    }

当然,最后的while循环困扰着我。在从movieListResponse方法返回之前,还有其他更优雅的方法等待响应被分配吗?

我尝试在Firebase get()方法返回的Task上调用await(),但无论如何movieListResponse方法都会立即返回。谢谢你的帮助!

3 个答案:

答案 0 :(得分:7)

因此,我在Google Tasks API中找到了所需的内容:“如果您的程序已经在后台线程中执行,则可以阻止任务以同步获取结果并避免回调” https://developers.google.com/android/guides/tasks#blocking

所以我以前有问题的代码变为:

private fun movieListResponse(): Call.Response<List<MovieEntity>> {
        return try {
            val taskResult = Tasks.await(FirebaseFirestore.getInstance().
                    collection(DataConfig.databasePath + MOVIES_PATH).get(), 2, TimeUnit.SECONDS)
            Call.Response(true, taskResult.mapTo(ArrayList()) { MovieEntity.fromSnapshot(it) }, null)
        } catch (e: ExecutionException) {
            Call.Response(false, null, Failure.ServerError())
        } catch (e: InterruptedException) {
            Call.Response(false, null, Failure.InterruptedError())
        } catch (e: TimeoutException) {
            Call.Response(false, null, Failure.TimeoutError())
        }
    }

注意,我不再需要Thread.sleep while循环。 此代码只能在后台线程/ kotlin协程中运行。

答案 1 :(得分:0)

这不是firebase的工作方式。 Firebase基于回调。

我推荐架构组件的liveata。

请检查以下示例。

这是一个链接:https://android.jlelse.eu/android-architecture-components-with-firebase-907b7699f6a0

答案 2 :(得分:0)

这是过度设计的,有几层试图做同样的事情。我建议你回过头几步,撤消抽象,直接使用协同程序。根据{{​​3}}实施suspend fun。您不需要Either的拐杖,以最自然的方式处理异常:围绕try-catch来电suspend fun

您最终应该签名如下:

suspend fun movieList(): List<MovieEntity>

致电网站:

launch(UI) {
    try {
        val list = movieList()
        ...
    } catch (e: FireException) {
        // handle
    }
}