Как обновить пользовательский интерфейс в сопрограммах в Kotlin 1.3

Я пытаюсь вызвать API, и когда мои переменные будут готовы, обновите компоненты пользовательского интерфейса соответственно.

Это мой сетевой синглтон, который запускает сопрограмму:

object MapNetwork {
    fun getRoute(request: RoutesRequest,
                 success: ((response: RoutesResponse) -> Unit)?,
                 fail: ((throwable: Throwable) -> Unit)? = null) {
        val call = ApiClient.getInterface().getRoute(request.getURL())

        GlobalScope.launch(Dispatchers.Default, CoroutineStart.DEFAULT, null, {

            try {
                success?.invoke(call.await())
            } catch (t: Throwable) {
                fail?.invoke(t)
            }

        })
    }
}

И вот как я это называю:

network.getRoute(request,
            success = {
                // Make Some UI updates
            },
            fail = {
                // handle the exception
            }) 

И я получаю Исключение, которое говорит, что не может обновить пользовательский интерфейс из любого потока, кроме потока пользовательского интерфейса:

com.google.maps.api.android.lib6.common.apiexception.c: Not on the main thread

Я уже пробовал это решение, но resume в Continuation<T> класс "устарел", так как Kotlin 1,3

2 ответа

Решение

Чтобы ответить на ваш ближайший вопрос, вы должны просто запустить сопрограмму в правильном контексте:

val call = ApiClient.getInterface().getRoute(request.getURL())
GlobalScope.launch(Dispatchers.Main) {
    try {
        success?.invoke(call.await())
    } catch (t: Throwable) {
        fail?.invoke(t)
    }
}

Однако это будет лишь верхушка айсберга, потому что ваш подход - неправильный способ использования сопрограмм. Их ключевое преимущество - избегать обратных вызовов, но вы их снова вводите. Вы также нарушаете передовую практику структурированного параллелизма, используя GlobalScope который не предназначен для производственного использования.

Очевидно, у вас уже есть асинхронный API, который дает вам Deferred<RoutesResponse> что ты можешь await на. Способ его использования заключается в следующем:

scope.launch {
    val resp = ApiClient.getInterface().getRoute(request.getURL()).await()
    updateGui(resp)
}

Вы можете быть огорчены тем фактом, что я предлагаю launch блокировать в каждом обратном вызове GUI, где вы должны выполнить приостановленный код, но на самом деле это рекомендуемый способ использования этой функции. Это находится в строгой параллели к письму Thread { ... my code ... }.start() потому что содержимое вашего launch Блок будет работать одновременно с кодом за его пределами.

Приведенный выше синтаксис предполагает, что у вас есть scope готовая переменная, которая реализует CoroutineScope, Например, это может быть ваш Activity:

class MyActivity : AppCompatActivity(), CoroutineScope {
    lateinit var masterJob: Job
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + masterJob

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        masterJob = Job()
    }

    override fun onDestroy() {
        super.onDestroy()
        masterJob.cancel()
    }
}

Обратите внимание, как coroutineContext устанавливает диспетчер сопрограмм по умолчанию Dispatchers.Main, Это позволяет использовать равнину launch { ... } синтаксис.

private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)

uiScope.launch {
            withContext(Dispatchers.IO) {
                //Do background tasks...
                withContext(Dispatchers.Main){
                    //Update UI
                }
            }
        }

Если вы используете сопрограммы-Android вы можете использовать Dispatchers.Main
(зависимость Gradle implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0")

network.getRoute(request,
        success = {
            withContext(Dispatchers.Main) {
                // update UI here
            }
        },
        fail = {
            // handle the exception
        }) 
Другие вопросы по тегам