Android 创建发出侦听器回调结果的Kotlin协同路由延迟对象

Android 创建发出侦听器回调结果的Kotlin协同路由延迟对象,android,kotlin,rx-java,kotlinx.coroutines,Android,Kotlin,Rx Java,Kotlinx.coroutines,我目前正在一个项目中从RxJava切换到Kotlin协程,用协程aquivalent替换所有单一和可观察的返回类型。我仍在为以下构造而挣扎:接口(例如存储库)提供数据查询访问并返回一个RxJava单变量。该实现使用Single.create创建单个对象,并使用onSuccess/onError发送结果。现在,实现需要做的是检索数据,创建一个带有回调的侦听器,并将该侦听器注册到某个对象。然后,所创建侦听器的回调将调用自制单上的onSuccess/onError。例如,使用firebase(尽管我的

我目前正在一个项目中从RxJava切换到Kotlin协程,用协程aquivalent替换所有单一和可观察的返回类型。我仍在为以下构造而挣扎:接口(例如存储库)提供数据查询访问并返回一个RxJava单变量。该实现使用Single.create创建单个对象,并使用onSuccess/onError发送结果。现在,实现需要做的是检索数据,创建一个带有回调的侦听器,并将该侦听器注册到某个对象。然后,所创建侦听器的回调将调用自制单上的onSuccess/onError。例如,使用firebase(尽管我的问题不是针对firebase):

接口存储库{
fun getData(查询:查询):单个
}
fun getData(query:query):Single=Single.create{emitter->
query.addListenerForSingleValueEvent(对象:ValueEventListener{
覆盖已取消(错误:DatabaseError?){
emitter.onError(异常())
}
覆盖数据更改(数据:DataSnapshot?){
发射器.onSuccess(数据)
}
})
}
现在我想要的是返回coroutine Deferred的接口方法。必须如何创建实现,以便还可以注册一个具有回调的侦听器,其结果随后将由延迟服务器交付?我看不到像async、launch等这样的Corroutine构建器可以做onSuccess/onError会做的事情

interface Repository {
    fun getData(query: Query): Deferred<DataSnapshot?>
}
接口存储库{
fun getData(查询:查询):延迟
}

我的建议如下:

interface Repository {
    suspend fun getData(query: Query): Result<DataSnapshot>
}

一般来说,在处理协同程序时,您应该避免返回延迟,并尝试将它们“用作同步方法”

编辑: 现在我了解了这个问题,我希望这有助于解决它:

//This is as generic as it gets, you could use it on any Query, no need to retype it
suspend fun Query.await(): DataSnapshot = suspendCoroutine{cont ->
    addListenerForSingleValueEvent(object : ValueEventListener{
        override fun onCancelled(error: DatabaseError?) {
            cont.resumeWithException(error?: Exception("Unknown Error"))
        }

        override fun onDataChange(data: DataSnapshot?) {
            if(data != null){
                cont.resume(data)
            } else {
                cont.resumeWithException(Exception("Null data"))
            }

        }
    })
}
//this is your actual implementation
suspend fun getData(query: Query):DataSnapshot =
        query.await()

此代码假定DatabaseError扩展了异常或Throwable。如果不是,你需要为它创建一个包装类型,或者使用我的原始解决方案,并在这两种情况下使用常规简历。

我认为我要寻找的最接近的东西是ReceiveChannel。提出了以下解决方案:

override fun getData(query: Query): ReceiveChannel<Datasnapshot?> = GlobalScope.produce {
    query.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(data: DataSnapshot?) {
            launch { send(data) } }
        }

        override fun onCancelled(error: DatabaseError?) {
            throw Exception()
        }
    })
}
override fun getData(query:query):ReceiveChannel=GlobalScope.product{
query.addListenerForSingleValueEvent(对象:ValueEventListener{
覆盖数据更改(数据:DataSnapshot?){
启动{send(data)}
}
覆盖已取消(错误:DatabaseError?){
抛出异常()
}
})
}

不确定这是否有点过分,是否会有更好的方法,欢迎建议

如果您正在从Rx切换到协同程序,那么您应该从单一功能转到挂起功能:挂起fun getData(查询:查询):DataSnapshot?根据经验,您不应该有返回延迟的函数。好的,我会这样做,但仍然不知道如何实现,如何从侦听器获取并回调到挂起函数所需的返回值DataSnapshot?仍然没有解决问题。我无法从作为挂起函数返回类型的回调方法中返回任何内容。这就是为什么Single.create构造首先是如何完成的。现在我对这个问题有了更好的理解。遗憾的是,我现在没有时间解释如何从这种情况转换为协同工作。但在2017年kotlin conf中有两次关于协同程序的讨论深入解释了它们,第二次讨论解释了如何做这类事情。
return Success(yourData)
return Error("Something went wrong")
//This is as generic as it gets, you could use it on any Query, no need to retype it
suspend fun Query.await(): DataSnapshot = suspendCoroutine{cont ->
    addListenerForSingleValueEvent(object : ValueEventListener{
        override fun onCancelled(error: DatabaseError?) {
            cont.resumeWithException(error?: Exception("Unknown Error"))
        }

        override fun onDataChange(data: DataSnapshot?) {
            if(data != null){
                cont.resume(data)
            } else {
                cont.resumeWithException(Exception("Null data"))
            }

        }
    })
}
//this is your actual implementation
suspend fun getData(query: Query):DataSnapshot =
        query.await()
override fun getData(query: Query): ReceiveChannel<Datasnapshot?> = GlobalScope.produce {
    query.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(data: DataSnapshot?) {
            launch { send(data) } }
        }

        override fun onCancelled(error: DatabaseError?) {
            throw Exception()
        }
    })
}