如何从 android kotlin 协程获取结果到 UI 线程

How get result to UI Thread from an android kotlin coroutines

我不明白 kotlin 协程是如何工作的。 我需要在异步线程上做很长时间的工作,并在 Android 应用程序的 UI 线程上获得结果。 有人可以给我一些例子吗? 例如

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

将午餐 model.getAllCountries() 异步但最后我如何才能将结果发送到 UI 线程?

根据 documentation for viewModelScope:

This scope is bound to Dispatchers.Main.immediate

其中 Dispatchers.Main 是 Kotlin 的说法 'the main thread'。这意味着,默认情况下,launch 块中的所有代码都会在主线程上阻塞 运行。你的 getAllCountries(),如果它想 运行 在不同的线程上,会想使用 withContext(Disptachers.IO) 移动到 IO 协程调度程序,例如。

因此在这种情况下,您的方法的结果已经在主线程上,您不需要做任何其他事情。

嗯!添加到@ianhanniballake 的回答,

在你的函数中,

private fun getCountries(){
   // 1
   viewModelScope.launch {  
      val a = model.getAllCountries()
      countriesList.value = a
   }
}
  1. 您已经从 viewModel 范围启动了 suspend 函数,默认上下文是主线程。

现在 suspend fun getAllCountries 将在其上工作的线程将在 getAllCountries 函数的定义中指定。

所以可以这样写

suspend fun getAllCountries(): Countries {
   // 2
   return withContext(Disptachers.IO) { 
    service.getCountries()
   }
}
  1. 我们使用 withContext 指定一个新线程来调用服务器,在 withContext 块的 return 之后,我们回到主线程。

I need to do a long work on an asynchronous thread

实际上,不存在异步线程这样的东西。您的网络操作是同步还是异步取决于您正在使用的网络 API 的实施。

如果您有一个阻塞的网络操作,即使您应用协同程序,它也会保持阻塞状态。该用例的协同程序的价值仅限于使将结果传输回 UI 线程变得更容易一些。

您可以通过使用 UI 调度程序(默认)启动协程然后切换到线程池来执行阻塞操作而不阻塞 UI 线程来实现此目的:

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

请注意,IO 调度程序下的线程池中的线程仍将被阻塞,因此就系统资源的使用而言,这没有什么不同。阻塞的本机线程将与并发网络调用一样多。

另一种解决方案是 post 在 ViewModel class 内的 MutableLiveData 中得到你的结果,并在你的视图中观察 LiveData。

你的 ViewModel class:

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(例如片段)

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)
            })
        }
    }