web-dev-qa-db-ja.com

Kotlinコルーチンへの既存の3機能コールバック

私は特定の例で一般的な質問があります:写真を撮るときにAndroidでコールバック地獄の代わりにKotlinコルーチンマジックを使用したいと思います。

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()
    }
    ...

どのようにそれを...エラー...よりlessいものに変換しますか? 3つ程度の関数で平均的なコールバックを取得し、プライマリフローをpromise-resultパスとして指定することでプロミスチェーンに変換することは可能ですか?もしそうなら、コルーチンを使用して非同期にする必要がありますか?

私は非同期と.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()
}
18
Benjamin H

この特定のケースでは、一般的なアプローチを使用して、コールバックベースの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)を実行し、正常に開かれた場合はCameraDeviceへの参照を取得でき、正常に開かれなかった場合はnullへの参照を取得できます。

44
Roman Elizarov

私はこの種のことに対して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
PeejWeej