Deconstructing the camera2 api using "by lazy" fights with callbacks/suspendCoroutine


tl/dr: Can “by lazy” be used in constructors that aren’t finished until a callback completes (which requires suspendCoroutine)?

I’m so close I can taste it!

The Android camera2 API isn’t easy - it requires a careful choreographed dance of callbacks, attaching listeners, waiting for onReady states, etc. So I tried a completely different experiment, breaking down everything into small “by lazy” chunks, and relying on the dependencies to magically initialize everything in the right order through a giant implied cascading dependency graph.

A bit crazy, but I had an excellent “deconstructed” dish at a fancy restaurant, and got inspired.

Most of it is easy. The builders that wrap callbacks are harder, I ended up with

/** The surface that the preview gets drawn on */
private val readySurface: Surface by lazy {
    runBlocking(bgThreadPoolContext) {
        suspendCoroutine { cont: Continuation<Surface> ->
            if (previewTextureView.isAvailable) {
                cont.resume(Surface(previewTextureView.surfaceTexture)).also {
                    Log.i(TAG, "Created readySurface directly")
            } else ....

But I get IllegalStateException: runBlocking is not allowed in Android main looper thread

Huh? Then where can I run it? Is what I’m trying to do impossible? I was very hopeful that the dependency graph would magically auto-resolve itself. I’m ok with the UI having to wait a few seconds until the camera spins up.

Public Gist that shows what I was attempting to do: