88

我有一个具体示例的一般性问题:我想在拍照时使用 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()
}
4

3 回答 3

229

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

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)的参考。 CameraDevicenull

于 2018-02-01T11:55:35.380 回答
0

我已经为这类事情使用了 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)
    }
}

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

于 2018-01-31T23:29:49.600 回答
0

使用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 传播取消。 来源与其他伟大的例子

于 2021-12-31T06:30:53.967 回答