Как получить результат в UI Thread от андроида kotlin сопрограмм - PullRequest
2 голосов
/ 07 октября 2019

Я не понял, как работают соплины kotlin. Мне нужно проделать большую работу в асинхронном потоке и получить результат в потоке пользовательского интерфейса в приложении для Android. Может кто-нибудь привести мне несколько примеров? Например,

private fun getCountries(){
        viewModelScope.launch { 
            val a = model.getAllCountries()
            countriesList.value = a
        }
}

будет обедать model.getAllCountries () асинхронно, но в конце концов, как я могу получить результат в потоке пользовательского интерфейса?

Ответы [ 4 ]

3 голосов
/ 07 октября 2019

Хорошо! Добавляя к ответу @ ianhanniballake,

В вашей функции

private fun getCountries(){
   // 1
   viewModelScope.launch {  
      val a = model.getAllCountries()
      countriesList.value = a
   }
}
  1. Вы запустили функцию suspend из области видимости viewModel, а контекст по умолчанию - основной поток.

Теперь поток, в котором будет работать suspend fun getAllCountries, будет указан в определении функции getAllCountries.

Так что можно написать что-то вроде

suspend fun getAllCountries(): Countries {
   // 2
   return withContext(Disptachers.IO) { 
    service.getCountries()
   }
}
Мы указываем новый поток для вызова сервера, используя withContext, и после возврата из блока withContext мы возвращаемся в основной поток.
2 голосов
/ 07 октября 2019

Согласно документации для viewModelScope:

Эта область привязана к Dispatchers.Main.immediate

Где Dispatchers.Main - это котлинский способ сказать «главная нить». Это означает, что по умолчанию весь код в блоке launch выполняется в основном потоке. Ваш getAllCountries(), если он хочет работать в другом потоке, хотел бы использовать withContext(Disptachers.IO) для перехода к диспетчеру сопрограмм ввода-вывода, например.

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

0 голосов
/ 08 октября 2019

Другим решением было бы опубликовать ваш результат в MutableLiveData внутри вашего класса ViewModel и наблюдать LiveData в вашем представлении.

Ваш ViewModel класс:

class CountriesViewModel : ViewModel() {
    private val parentJob = Job()
    val coroutineContext: CoroutineContext
        get() = parentJob + Dispatchers.Default
    val viewModelScope = CoroutineScope(coroutineContext)
    val countries: MutableLiveData<ArrayList<Country>> = MutableLiveData()
    val model = MyModel()

    fun getCountries(){
        viewModelScope.launch {
            val countriesList = model.getAllCountries()
            countries.postValue(countries)
        }
    }
}

Ваш вид класс (например, фрагмент)

class CountriesFragment : Fragment(){
        private lateinit var countriesVM : CountriesViewModel
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            countriesVM = ViewModelProviders.of(this).get(CountriesViewModel::class.java)
            // calling api in your view model here
            countriesVM.getCountries()
        }

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            // observer is notified of the changes on countries livedata
            countriesVM.countries.observe(this, Observer { countries ->
                // Update ui here
                updateUI(countries)
            })
        }
    }
0 голосов
/ 08 октября 2019

Мне нужно проделать большую работу в асинхронном потоке

На самом деле асинхронного потока не существует. Независимо от того, являются ли ваши сетевые операции синхронизированными или асинхронными, определяется реализацией используемого вами сетевого API.

Если у вас есть блокирующая сетевая операция, она останется блокированной даже при применении сопрограмм. Значение сопрограмм для этого варианта использования ограничено упрощением передачи результата обратно в поток пользовательского интерфейса.

Этого можно добиться, запустив сопрограмму с диспетчером пользовательского интерфейса (по умолчанию) и затем переключивв пул потоков для выполнения операции блокировки без блокировки потока пользовательского интерфейса:

    viewModelScope.launch { 
        countriesList.value = withContext(Dispatchers.IO) {
            model.getAllCountries()
        }
    }

Обратите внимание, что поток в пуле потоков, лежащий в диспетчере IO, по-прежнему будет заблокирован, поэтому с точки зрения использованиясистемные ресурсы это не имеет значения. Будет заблокировано столько же собственных потоков, сколько и одновременных сетевых вызовов.

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