Котлин: Как выполнить асинхронную функцию и дождаться ее завершения? - PullRequest
2 голосов
/ 26 июня 2019

Я пишу свое первое приложение на Kotlin, так что я довольно новичок в этом. Одной из функций, которую он выполняет, является чтение чего-либо из API, а затем обновление экрана в зависимости от результата.

Я много чего перепробовал с сопрограммами, но, похоже, ничего не работает.

Например, у меня есть что-то вроде этого:

private fun readAPI() {
    runBlocking {
        fun rAPI() = async {
            val api = "..."
            result = URL(api).readText()
        }
        println(tag, "Result: " + rAPI().await())
    }
}

И много разных подходов. Ничто не похоже на работу. В приведенном выше случае я получаю исключение "android.os.NetworkOnMainThreadException".

Единственное, что до сих пор работало, это что-то, использующее OkHttp3, как описано здесь: https://rstopup.com/como-hacer-una-solicitud-a-la-api-de-kotlin.html (это на испанском, но вы поймете идею), и это работает, оно дает ответ API, Я анализирую его, заполняю базу данных sqlite3 и так далее. Но, поскольку я не знаю, когда закончится API, я не могу обновить элементы управления экраном. И если я пытаюсь сделать это последовательно, я получаю исключение, что только поток, который запустил действие, может обновить действие или что-то в этом роде.

Я видел и следую МНОЖЕСТВУ учебных пособий, в которых говорится о функциях приостановки, запуска и т. Д., И они пытаются имитировать вызов API с помощью delay(), эти учебные пособия работают отлично, пока я не попытаюсь создать настоящий API звоните.

Итак, можете ли вы привести мне полный пример того, как вызвать API с Kotlin, а затем обновить некоторые элементы экрана?

EDIT Я редактирую, изменяя веселье по val:

runBlocking {
  val rAPI = async {
    val api = "..."
    URL(api).readText()
  }
  Log.w(tag, rAPI.await())
}

Я получил исключение "android.os.NetworkOnMainThreadException".

1 Ответ

1 голос
/ 26 июня 2019

Так как вы хотите использовать coroutine - async пути, вы должны сообщить основному потоку, что он ждет вас.вам нужно использовать suspend функцию или блок для этого.

GlobalScope.launch {
    suspend {
        Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
        delay(10000)
        withContext(Dispatchers.Main) {
            Log.d("coroutineScope", "#runs on ${Thread.currentThread().name}")
        }
    }.invoke()
}

Журнал результатов

09:36:09.500 D/: #runs on DefaultDispatcher-worker-1
// 10 seconds later
09:36:19.678 D/: #runs on main 

Я думаю, что это должно сработать.

Однако япредлагаю вам понять, как использовать OkHttp / Volley, передавая обратные вызовы (с onSuccess и onFail или что-то) в это.Или Retrofit2 с RxJava для решения многих из этих проблем.

EDIT В случае ошибки Module with the Main dispatcher had failed to initialize замените строку withContext() на эту

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