Почему содержимое некоторых вызовов GlobalScope.launch в Kotlin молча не может быть выполнено? - PullRequest
0 голосов
/ 20 февраля 2019

Я новичок в Kotlin - фактически я никогда не планировал его использовать, но я пытаюсь использовать библиотеку CameraKit , которая использует, как представляется, сгенерированный слой Kotlin для своего интерфейса API.У меня были проблемы с неправильным отключением камеры, рассматриваемый код (или, по крайней мере, то, что мне кажется наиболее значимым в нем) выглядит так:

class CameraPreview : FrameLayout, CameraEvents {
    var TAG = "CameraPreview.kt"
    var lifecycleState: LifecycleState = LifecycleState.STOPPED
    var surfaceState: SurfaceState = SurfaceState.SURFACE_WAITING
    var cameraState: CameraState = CameraState.CAMERA_CLOSED
        set(state) {
            field = state
            when (state) {
                CameraState.CAMERA_OPENED -> {
                    listener?.onCameraOpened()
                }
                CameraState.PREVIEW_STARTED -> {
                    listener?.onPreviewStarted()
                }
                CameraState.PREVIEW_STOPPED -> {
                    listener?.onPreviewStopped()
                }
                CameraState.CAMERA_CLOSING -> {
                    listener?.onCameraClosed()
                }
                else -> {
                    // ignore
                }
            }
        }


    private val cameraDispatcher: CoroutineDispatcher = newSingleThreadContext("CAMERA")
    private var cameraOpenContinuation: Continuation<Unit>? = null
    private var previewStartContinuation: Continuation<Unit>? = null


    init {
        val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        displayOrientation = windowManager.defaultDisplay.rotation * 90

        cameraSurfaceView.cameraSurfaceTextureListener = object : CameraSurfaceTextureListener {
            override fun onSurfaceReady(cameraSurfaceTexture: CameraSurfaceTexture) {
                surfaceTexture = cameraSurfaceTexture
                surfaceState = SurfaceState.SURFACE_AVAILABLE
                if (lifecycleState == LifecycleState.STARTED || lifecycleState == LifecycleState.RESUMED) {
                    resume()
                }
            }
        }

        addView(cameraSurfaceView)
    }

    fun start(facing: CameraFacing) {
        GlobalScope.launch(cameraDispatcher) {
            runBlocking {
                Log.i(TAG, "Start preview state is "+lifecycleState)
                lifecycleState = LifecycleState.STARTED
                cameraFacing = facing
                openCamera()
            }
        }
    }

    fun resume() {
        GlobalScope.launch(cameraDispatcher) {
            runBlocking {
                Log.i("CameraPreview.kt", "Resume preview state is "+lifecycleState)
                lifecycleState = LifecycleState.RESUMED
                try {
                    startPreview()
                } catch (e: Exception) {
                    Log.i("CameraPreview.kt", "Start preview hit an exception: "+e.message)
                    // camera or surface not ready, wait.
                }
            }
        }
    }

    fun pause() {
        Log.i("CameraPreview.kt", "Pause called");
        GlobalScope.launch(cameraDispatcher) {
            Log.i(TAG, "Pause scope launched, runblocking call ahead.")
            runBlocking {
                Log.i("CameraPreview.kt", "Pause initiated, stop preview state is "+lifecycleState)
                lifecycleState = LifecycleState.PAUSED
                stopPreview()
            }  
        }

    }

    fun stop() {
        Log.i("CameraPreview.kt", "Stop called");
        GlobalScope.launch(cameraDispatcher) {
            Log.i(TAG, "Stop scope launched, runblocking call ahead.")
            runBlocking {
                Log.i("CameraPreview.kt", "Stop initiated, close camera state is "+lifecycleState)
                lifecycleState = LifecycleState.STOPPED
                closeCamera()
            }

        }

    }



    enum class CameraState {
        CAMERA_OPENING,
        CAMERA_OPENED,
        PREVIEW_STARTING,
        PREVIEW_STARTED,
        PREVIEW_STOPPING,
        PREVIEW_STOPPED,
        CAMERA_CLOSING,
        CAMERA_CLOSED;
    }

    // Camera control:

    private suspend fun openCamera(): Unit = suspendCoroutine {
        cameraOpenContinuation = it
        cameraState = CameraState.CAMERA_OPENING
        Log.i("CameraPreview.kt", "openCamera call state is "+lifecycleState)
        cameraApi.open(cameraFacing)
    }

    private suspend fun startPreview(): Unit = suspendCoroutine {
        Log.i("CameraPreview.kt", "startPreview, lifecyclestate "+ lifecycleState);
        // do stuff

    }

    private suspend fun stopPreview(): Unit = suspendCoroutine {
        Log.i("CameraPreview.kt", "Stop preview state is "+lifecycleState)
        cameraState = CameraState.PREVIEW_STOPPING
        cameraApi.stopPreview()
        it.resume(Unit)
    }

    private suspend fun closeCamera(): Unit = suspendCoroutine {
        Log.i("CameraPreview.kt", "Close camera state is "+lifecycleState)
        cameraState = CameraState.CAMERA_CLOSING
        cameraApi.release()
        it.resume(Unit)
    }


}

Теперь, когда startи resume называются, они делают то, что должны.При вызове pause и stop ощутимых изменений не происходит, и предварительный просмотр камеры не приостанавливается и не останавливается.

Моя запись в журнале указывает, что при вызове pause() первоначальный вызов регистрируется, нооператор журнала внутри GlobalScope.launch(cameraDispatcher) никогда не появляется.Кажется, что GlobalScope просто запускает подпрограмму в пустоту.Поскольку start и resume работают, кажется, что нет проблем с вызовом или диспетчером, но трудно знать, где еще искать

Если я возьму содержимоеpause или stop подпрограмма и поместите ее вне GlobalScope.launch, чтобы создать что-то вроде этого:

fun pause() {
    Log.i("CameraPreview.kt", "Pause called");
    GlobalScope.launch(cameraDispatcher) {
        Log.i(TAG, "Pause scope launched, runblocking call ahead.")
        runBlocking {
            Log.i("CameraPreview.kt", "Pause initiated, stop preview state is "+lifecycleState)
            lifecycleState = LifecycleState.PAUSED
            stopPreview()
        }
    }
    cameraState = CameraState.PREVIEW_STOPPING
    cameraApi.stopPreview()
}

Мало того, что теперь это работает, но подпрограмма также начинает работать над последующимизвонки, как будто содержание того, что вызывалось, каким-то образом запуталось.Я не вижу сообщений об ошибках в Logcat, когда он работает.

Это мое первое знакомство с Kotlin, поэтому я не совсем уверен, как он должен работать, но кажется, что полный обход существенного кода может не сработать.быть частью спецификации.Кто-нибудь может подсказать, что здесь происходит или как я могу отследить истинный источник проблемы?

1 Ответ

0 голосов
/ 20 февраля 2019

Возможно, проблема в том, что вы используете общий однопоточный диспетчер с cameraDispatcher, который затем блокируется с помощью runBlocking.Я не уверен, на чем основан этот код, но в документации runBlocking есть предупреждение о том, что его следует использовать только при соединении между сопрограммами и строго ограниченным потоком выполнением, таким как main или инфраструктурой тестирования.

Ваш код все еще должен работать, если вы удалите все вызовы на runBlocking

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...