如何从安卓 kotlin 协程获取 UI 线程的结果



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

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

午餐模型.getAllNations(( 是异步的,但最终我怎样才能将结果发送到 UI 线程?

好吧!加上@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(Dispatchers.IO) { 
service.getCountries()
}
}
    我们
  1. 指定一个新线程来使用withContext调用服务器,从withContext块返回后,我们回到主线程上。

根据viewModelScope的文档:

此作用域绑定到 Dispatchers.Main.immediate

其中Dispatchers.Main是 Kotlin 所说的"主线"的方式。这意味着,默认情况下,launch块中的所有代码都在主线程上运行。例如,您的getAllCountries()如果要在不同的线程上运行,则需要使用withContext(Disptachers.IO)移动到 IO 协程调度程序。

因此,在这种情况下,方法的结果已经在主线程上,您无需执行任何其他操作。

我需要在异步线程上做很长时间的工作

实际上,没有异步线程这样的东西。网络操作是同步还是异步,取决于所使用的网络 API 的实现。

如果存在阻塞网络操作,则即使应用协程,它也会保持阻塞状态。该用例的协程值仅限于使将结果传输回 UI 线程变得更加容易。

为此,您可以使用 UI 调度程序(默认(启动协程,然后切换到线程池以执行阻塞操作而不阻塞 UI 线程:

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

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

另一种解决方案是将结果发布在 ViewModel 类的MutableLiveData中,并在视图中观察 LiveData。

您的视图模型类:

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

最新更新