【问题标题】:Existing 3-function callback to Kotlin CoroutinesKotlin Coroutines 的现有 3 函数回调
【发布时间】:2018-07-11 04:45:25
【问题描述】:

我有一个关于具体示例的一般性问题:我想在拍照时使用 Kotlin 协程魔法而不是 Android 中的回调地狱。

manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
    override fun onOpened(openedCameraDevice: CameraDevice) {
        println("Camera onOpened")
        // even more callbacks with openedCameraDevice.createCaptureRequest()....
    }

    override fun onDisconnected(cameraDevice: CameraDevice) {
        println("Camera onDisconnected")
        cameraDevice.close()
    }
    ...

我如何将其转换为不那么丑陋的东西? 是否可以通过将主要流程指定为 promise-result 路径来将平均回调与三个左右的函数转换为 promise-chain? 如果那么,我应该/应该使用协程使其异步吗?

我喜欢 async 和 .await 的结果

manager.open(cameraId).await().createCaptureRequest()

我正在尝试通过以下方式进行操作,但我认为我使用的不是CompletableDeferred 对!

suspend fun CameraManager.open(cameraId:String): CameraDevice {
    val response = CompletableDeferred<CameraDevice>()
    this.openCamera(cameraId, object : CameraDevice.StateCallback() {
        override fun onOpened(cameraDevice: CameraDevice) {
            println("camera onOpened $cameraDevice")
            response.complete(cameraDevice)
        }

        override fun onDisconnected(cameraDevice: CameraDevice) {
            response.completeExceptionally(Exception("Camera onDisconnected $cameraDevice"))
            cameraDevice.close()
        }

        override fun onError(cameraDevice: CameraDevice, error: Int) {
            response.completeExceptionally(Exception("Camera onError $cameraDevice $error"))
            cameraDevice.close()
        }
    }, Handler())
    return response.await()
}

【问题讨论】:

  • 如果有多个回调按顺序,则链式回调起作用,每个回调都提供一个结果或错误。 这里有两个并行的回调,你如何想象一次链接两个回调?您的样本选择哪一个? 哦,主要流程。但是onDisconnected还是需要关闭,怎么上链呢?

标签: android kotlin kotlin-coroutines


【解决方案1】:

我已经为这类事情使用了 2 个解决方案。

1:将接口包装在扩展中

CameraDevice.openCamera(cameraId: Integer, 
                onOpenedCallback: (CameraDevice) -> (), 
          onDisconnectedCallback: (CameraDevice) ->()) {

    manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
        override fun onOpened(openedCameraDevice: CameraDevice) {
            onOpenedCallback(openedCameraDevice)
        }

        override fun onDisconnected(cameraDevice: CameraDevice) {
            onDisconnectedCallback(cameraDevice)
        }
   })
}

2:制作一个接口更实用的简单容器类:

class StateCallbackWrapper(val onOpened: (CameraDevice) -> (), val onClosed: (CameraDevice) ->()): CameraDevice.StateCallback() {
    override fun onOpened(openedCameraDevice: CameraDevice) {
        onOpened(openedCameraDevice)
    }

    override fun onDisconnected(cameraDevice: CameraDevice) {
        onClosed(cameraDevice)
    }
}

我个人会从类似的东西开始,然后在此之上构建任何线程差异。

【讨论】:

  • 我不清楚:这能给我带来什么?它调用回调,所以我仍然需要提供某种回调,所以它所做的就是将单个回调(包含 2 个函数)拆分为调用一个方法并传入 2 个不同的函数。除非那会启用.await()?
  • 在使用时,您传递的是 lamdas 或函数引用,而不必创建整个对象。回调可能已经是异步的。你只是想创建一个管理整个过程的类吗?
  • 回调是异步的,但我正试图摆脱回调地狱,进入可以链接默认路径的函数的地方。就像manager.open(cameraId).capture().saveFile() 一样,这需要我阻止 manager.open(cameraId) 调用某个值被填充(我认为应该使用 CompletableDeferred?)将它们捆绑在一起。
  • 间接的经典例子,问题只是在修复的幌子下被转移。我们都时不时地这样做。至少我会。
【解决方案2】:

在这种特殊情况下,您可以使用通用方法通过suspendCoroutine 函数将基于回调的 API 转换为挂起函数:

suspend fun CameraManager.openCamera(cameraId: String): CameraDevice? =
    suspendCoroutine { cont ->
        val callback = object : CameraDevice.StateCallback() {
            override fun onOpened(camera: CameraDevice) {
                cont.resume(camera)
            }

            override fun onDisconnected(camera: CameraDevice) {
                cont.resume(null)
            }

            override fun onError(camera: CameraDevice, error: Int) {
                // assuming that we don't care about the error in this example
                cont.resume(null) 
            }
        }
        openCamera(cameraId, callback, null)
    }

现在,在您的应用程序代码中,您只需执行 manager.openCamera(cameraId) 并获取对 CameraDevice 的引用(如果它已成功打开)或 null 如果未成功打开。

【讨论】:

  • 这种情况下如何处理协程取消?
  • @Bolein95:改用suspendCancellableCoroutine
  • 您可以选择用异常表示您的错误并执行cont.resumeWithException(CameraException(error)) 或使用特殊结果类型表示您的错误并执行cont.resume(CameraErrorResult(error))
  • 这简直太完美了!我
  • 请注意,如果回调触发不止一次,这将崩溃,例如在某些情况下旋转设备时
【解决方案3】:

使用suspendCancellableCoroutine代替suspendCoroutine并进行适当的异常处理

suspend fun CameraManager.openCamera(cameraId: String): CameraDevice? =
        suspendCancellableCoroutine { cont ->
             val callback = object : CameraDevice.StateCallback() {
                 override fun onOpened(camera: CameraDevice) {
                   cont.resume(camera)
               }

               override fun onDisconnected(camera: CameraDevice) {
                   cont.resume(null)
               }

               override fun onError(camera: CameraDevice, error: Int) {
                   // Resume the coroutine by throwing an exception or resume with null
                   cont.resumeWithException(/* Insert a custom exception */) 
               }
        }
        openCamera(cameraId, callback, null)
    }

最好始终选择 suspendCancellableCoroutine 来处理协程范围的取消,或者从底层 API 传播取消。 Source with other great examples

【讨论】:

    猜你喜欢
    • 2023-04-04
    • 2020-07-27
    • 2021-10-13
    • 2019-11-28
    • 2019-10-17
    • 1970-01-01
    • 2020-10-15
    • 2019-10-22
    • 1970-01-01
    相关资源
    最近更新 更多