Можно ли приостановить coroutine в инициализаторе" by lazy"? Я получаю ошибки " runBlocking is not allowed in Android main looper thread"
У меня большая часть моего приложения отлично работает с" ленивыми " инициализаторами, потому что все волшебным образом происходит в нужном порядке.
, но не все инициализаторы являются синхронными. Некоторые из них оборачивают обратные вызовы, что означает, что мне нужно подождать, пока обратный вызов не произойдет, что означает, что мне нужны runBlocking
и suspendCoroutine
.
Но после рефакторинга всего, я получаю это IllegalStateException: runBlocking is not allowed in Android main looper thread
Что? Ты не можешь блокировать? Ты убиваешь меня здесь. Какой же правильный путь, если мой " ленивый" может быть, это блокирующая функция?
private val cameraCaptureSession: CameraCaptureSession by lazy {
runBlocking(Background) {
suspendCoroutine { cont: Continuation<CameraCaptureSession> ->
cameraDevice.createCaptureSession(Arrays.asList(readySurface, imageReader.surface), object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
cont.resume(session).also {
Log.i(TAG, "Created cameraCaptureSession through createCaptureSession.onConfigured")
}
}
override fun onConfigureFailed(session: CameraCaptureSession) {
cont.resumeWithException(Exception("createCaptureSession.onConfigureFailed")).also {
Log.e(TAG, "onConfigureFailed: Could not configure capture session.")
}
}
}, backgroundHandler)
}
}
}
Полная суть класса, чтобы получить представление о том, что я первоначально пытался сделать: https://gist.github.com/salamanders/aae560d9f72289d5e4b49011fd2ce62b
1 ответ:
Хорошо известно, что выполнение блокирующего вызова в потоке пользовательского интерфейса приводит к полной заморозке приложения на время выполнения вызова. В документации
createCaptureSession
конкретно говоритсяДля завершения настройки сеанса может потребоваться несколько сотен миллисекунд, поскольку аппаратное обеспечение камеры может потребоваться включить или перенастроить.Это может очень легко привести к появлению диалога
Поэтому ваша идея запустить этот процесс как раз вовремя, когда вы уже пытались получить доступ кApplication Not Responding
, и ваше приложение будет убито. Вот почему Котлин ввел явная защита отrunBlocking
в потоке пользовательского интерфейса.cameraCaptureSession
, не может сработать. Вместо этого вы должны обернуть код, который обращается к нему, вlaunch(UI)
и превратить вашval
вsuspend fun
.В двух словах:
Обратите внимание, чтоprivate var savedSession: CameraCaptureSession? = null private suspend fun cameraCaptureSession(): CameraCaptureSession { savedSession?.also { return it } return suspendCoroutine { cont -> cameraDevice.createCaptureSession(listOf(readySurface, imageReader.surface), object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) { savedSession = session Log.i(TAG, "Created cameraCaptureSession through createCaptureSession.onConfigured") cont.resume(session) } override fun onConfigureFailed(session: CameraCaptureSession) { Log.e(TAG, "onConfigureFailed: Could not configure capture session.") cont.resumeWithException(Exception("createCaptureSession.onConfigureFailed")) } }) } } fun useCamera() { launch(UI) { cameraCaptureSession().also { session -> session.capture(...) } } }
session.capture()
является еще одной целью для обертывания вsuspend fun
.Также не забудьте отметить, что код, который я дал, безопасен только в том случае, если вы можете гарантировать, что не будете звонить.
cameraCaptureSession()
еще раз, прежде чем первый звонок возобновится. Проверьте поток followup для более общего решения, которое заботится об этом.