1. ホーム
  2. android

[解決済み] Kotlinコルーチンへの既存の3機能コールバック

2023-04-28 19:29:17

質問

一般的な質問と具体的な例を挙げます。 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()
    }
    ...

どうすれば醜くないものに変換できるのでしょうか? 3つくらいの関数からなる平均的なコールバックを、主フローをプロミス結果のパスとして指定することでプロミスチェーンにすることは可能でしょうか? そして、もしそうなら、私はそれを非同期にするためにコルーチンを使うべきですか/そうですか?

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

どのように解決するのですか?

この特定のケースでは、コールバックベースの 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 となります。