【问题标题】:Nested Callback function with coroutines带有协程的嵌套回调函数
【发布时间】:2021-04-07 16:10:31
【问题描述】:

我想使用 kotlin 协程从回调函数 async/await 样式的 javascript 中获得响应。

这是我的回调函数

offlineCatalog.findOfflineVideoById(id, object : OfflineCallback<Video> {
     override fun onSuccess(video: Video?) {
         video?.let { 
              //Return This Video
         } ?: kotlin.run { 
              findVideoOnline(id, state) 
         }
    }
    override fun onFailure(throwable: Throwable?) {
         findVideoOnline(id, state)
    }
})

onlineCatalog.findVideoByID(id, object : VideoListener() {
    override fun onVideo(video: Video?) {
         video?.let { 
             //Return This Video 
         } ?: kotlin.run { 
            Log.e("Return Error") 
         }
    }
    override fun onError(errors: MutableList<CatalogError>) {
         super.onError(errors)
         Log.e("Return Error")
    }
})

如果OfflineCatalog 出错,我想调用将从OfflineCatalog 返回视频对象的函数,然后从OnlineCatalog 搜索。

比如

 try{
     val video:Video? = getVideo(id:String)
     //do something 
 }catch(throwable:Throwable){
     Log.e("Video not found")
 }

更新:我的实施n

这是我想出来的

suspend fun getVideo(id: String): Video? = withContext(Dispatchers.IO) {
    var video = getVideoOffline(id)
    video?.let { video } ?: kotlin.run { getVideoOnline(id) }
}

suspend fun getVideoOffline(id: String): Video? = suspendCancellableCoroutine { cont ->
    (offlineCatalog.findOfflineVideoById(id, object : OfflineCallback<Video> {
        override fun onSuccess(video: Video?) = cont.resume(video)
        override fun onFailure(throwable: Throwable?) = cont.resume(null)
    }))
}

suspend fun getVideoOnline(id: String): Video? = suspendCancellableCoroutine { cont ->
    catalog.findVideoByID(id, object : VideoListener() {
        override fun onVideo(video: Video?) = cont.resume(video)
        override fun onError(errors: MutableList<CatalogError>) = cont.resume(null)
    })
}

用法-

CoroutineScope(Dispatchers.Main).launch {
    getVideo(id)?.let { 
        //Do Something
    } ?: kotlin.run{
        //Video Not Found
    }
}

【问题讨论】:

  • 你应该让findOfflineVideoByIdfindVideoByID挂起函数
  • 但我不能在回调中调用 findVideoByid 因为我们必须在那里调用 resume
  • 显示这些函数
  • 这些是来自 Brightcove 播放器的预建函数。 Catalog & OfflineCatalog
  • 好的,那么getVideo函数会有点复杂..

标签: android kotlin-coroutines


【解决方案1】:

你必须这样做

@ExperimentalCoroutinesApi
suspend fun getVideo(id: String): Video? = coroutineScope {
    val offlineVideo: Video? = suspendCancellableCoroutine { cont ->
        offlineCatalog.findOfflineVideoById(id, object : OfflineCallback<Video> {
            override fun onSuccess(video: Video?) {
                cont.resume(video)
            }
            override fun onFailure(throwable: Throwable?) {
                cont.resume(null)
            }
        })
    }
    offlineVideo ?: suspendCancellableCoroutine { cont ->
        // offlineVideo not found so search from onlineCatalog
        onlineCatalog.findVideoByID(id, object : VideoListener() {
            override fun onVideo(video: Video?) {
                cont.resume(video)
            }

            override fun onError(errors: MutableList<CatalogError>) {
                super.onError(errors)
                cont.resumeWithException(someException)
            }
        })
    }
}

那么你可以随意调用它

someScope.launch {
    try {
        val video: Video? = getVideo(id)
        //do something
    } catch (throwable: Throwable) {
        Log.e("Video not found")
    }
}

阅读更多关于suspendCancellableCoroutinehere

【讨论】:

  • 我做了类似的事情,我添加到问题中。你能看看吗?我在 ViewHolder 中执行此操作,那么哪个范围比较好?
  • 您的实现在技术上是相同的,但这些范围函数是不必要的。 video?.let { video } ?: kotlin.run { getVideoOnline(id) } 应该只是 video ?: getVideoOnline(id)。在 viewHolder 你应该使用 activity/fragment.lifecycleScope 即(itemView.context as AppCompatActivity).lifecycleScope
  • 但是我在片段中使用它来在这里使用活动生命周期好吗?
  • 然后使用fragment.lifecycleScope。要关注best practices for coroutines,您应该像这样注入它class MyRecyclerAdapter(val scope: CoroutineScope)
猜你喜欢
  • 2016-06-16
  • 2020-01-22
  • 2018-06-27
  • 2020-10-09
  • 1970-01-01
  • 1970-01-01
  • 2020-11-22
  • 1970-01-01
  • 2018-10-17
相关资源
最近更新 更多