Блок кода в сопрограмме с использованием Dispatchers.Main не запускается - PullRequest
4 голосов
/ 13 мая 2019

Я искал везде, и я не нашел ничего, что могло бы решить мою проблему

У меня есть функция, использующая сопрограммы:

fun onAuthenticated() {
    launch (Dispatchers.IO) {
        userRepo.retrieveSelf()!!.let { name ->
            userRepo.addAuthenticatedAccount(name)
            userRepo.setCurrentAccount(name)
        }

        activity?.setResult(Activity.RESULT_OK, Intent())

        // this block doesn't seem to be run
        withContext(Dispatchers.Main) {
            Log.d(TAG, "ok looks gucci")
            activity?.finish()
        }
    }
}

Когда эта функция вызывается, код в блоке withContext(Dispatchers.Main) { ... } не запускается. Я использую его для доступа к активности в главном потоке.

Я был немного разочарован, и я не уверен, не понимаю ли я, как должен работать диспетчер / сопрограмма, или я что-то упускаю.

Дайте мне знать, если вам нужны какие-либо дополнительные данные или код!

EDIT Так что Марко был прав. После того, как я переместил activity.?.setResult(Activity.RESULT_OK, Intent()) так, чтобы он запускался с главным диспетчером, я обнаружил, что в userRepo.setCurrentAccount(name) была другая часть кода, которая вызывала проблему. После обновления кода, как показано ниже, он работает как положено!

override fun onAuthenticated() {
    val handler = CoroutineExceptionHandler { _, e ->
        Snackbar.make(
            web_auth_rootview,
                "Authentication unsuccessful",
                Snackbar.LENGTH_INDEFINITE
            ).show()
    }

    launch(Dispatchers.Main + handler) {
        userRepo.retrieveSelf()!!.let { name ->
            userRepo.addAuthenticatedAccount(name)
            userRepo.setCurrentAccount(name)
        }

        activity?.apply {
            setResult(Activity.RESULT_OK, Intent())
            onBackPressed()
        }
    }
}      

Большое спасибо Марко за помощь!

1 Ответ

1 голос
/ 13 мая 2019
activity?.setResult(Activity.RESULT_OK, Intent())

Здесь вы пытаетесь прикоснуться к компоненту GUI из потока ввода-вывода.Это, вероятно, вызывает исключение, но, поскольку он находится в потоке ввода-вывода, ничто его не ловит.

Вы можете обернуть все в try-catch, но ваша программа автоматически будет работать лучше, если вы используете правильную идиому, котораяна launch в диспетчере Main и переключитесь только в контекст ввода-вывода для операций блокировки:

launch(Dispatchers.Main) {
    withContext(Dispatchers.IO) {
        userRepo.retrieveSelf()!!.let { name ->
            userRepo.addAuthenticatedAccount(name)
            userRepo.setCurrentAccount(name)
        }
    }
    activity?.setResult(Activity.RESULT_OK, Intent())
    Log.d(TAG, "ok looks gucci")
    activity?.finish()
}

Теперь, если вы получите исключение в диспетчере ввода-вывода, оно будет распространяться на верхнийУровень сопрограммы, который вызовет исключение в основном потоке, и ваше приложение будет зависать с ним.Это прочная основа для добавления вашей логики обработки ошибок поверх.

Конечно, это все-таки не тот способ, которым вы должны работать с сопрограммами, потому что вы упускаете аспект структурированного параллелизма.

...