Переключение на контекст пользовательского интерфейса в сопрограммах - PullRequest
0 голосов
/ 11 февраля 2019

Я новичок в сопрограммах и мне интересно, возможно ли переключиться с coroutineScope (GlobalScope) на область пользовательского интерфейса для кода ниже.Моя проблема заключается в том, что шаги внутри тела запуска сопрограммы должны выполняться в рабочем потоке, иначе уведомление слушателя должно выполняться в потоке пользовательского интерфейса, чтобы избежать вызова runOnUiThread в моем коде активности.

override suspend fun startRent(name: String, bikeMode: BikeMode, listener: StartRentListener) {
        var bleDevice : RxBleDevice
        val scanFilter: ScanFilter = ScanFilter.Builder().setDeviceName(name).build()
        val scanSettings: ScanSettings = ScanSettings.Builder().build()

        val job = GlobalScope.launch {
            try {
                bleDevice = rxBleClient.scanBleDevicesExt(rxBleClient, scanSettings, scanFilter)
                val bleConnection = bleDevice.establishConnectionExt()
                // write handshake
                connectionManager.writeHandshake(bleDevice, bleConnection)
                // open lock
                openLock(bleDevice, bikeMode, bleConnection)
                // getting user position
                apiHelper.sendLockRequest(bleDevice.name, getPosition())
                bleDevice.disconnect()
                // this should be called on main thread once all the previous operations are finished
                listener.onSuccess()
            } catch (e: Exception) {
                listener.onError(e)
            }
        }
        job.join()
    }

Фрагмент моего текущего кода активности:

bikeAccessClient.startRent(bikeBLEName, BikeMode.HYBRID, object :
                    StartRentListener {
                    override fun onSuccess() {
                        runOnUiThread {
                            // UI update here
                        }
                    }

1 Ответ

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

Вы можете использовать функцию withContext(Dispatchers.UI) {..} для выполнения части вашего кода с другим Coroutine Dispatcher.

kotlinx.coroutines.android содержит определение функции Dispatchers.UI и корректно интегрируется с пользовательским интерфейсом Android.

Использование явного Dispatcher в вашем коде весьма подвержено ошибкам.Вместо этого я бы рекомендовал проектировать код с меньшим количеством явных требований.

Я бы написал что-то вроде этого:

fun uiActionHandlerToStartTheProcess() {
  launch(Dispatchers.Main) {
     val result = startRent(...) // no callback here, suspend function

     //UI Update Here
   }
}
suspend fun CoroutineScope.startRent() : SomeResultOfWork {
   //that function offloads the execution to a IO (aka brackground) thread
   return withContext(Dispatchers.IO){
     //here goes your code from `startRent`
     //use `suspendCancellableCoroutine {cont -> .. }` if you need to handle callbacks from it

     SomeResultOfWork()
   } 

Код в блоке launch(Dispatchers.Main){..} выполняется в потоке пользовательского интерфейса.Вызов функции startRent suspend приостанавливает выполнение в потоке пользовательского интерфейса.Как только startRent готов с ответом (из фонового потока), он возобновляет выполнение (которое выполняется Dispatchers.Main и эквивалентно runOnUiThread {...}) и выполняет обновление пользовательского интерфейса из правого потока

...