如何从Android Kotlin协程获取结果到UI线程

时间:2019-10-07 18:47:46

标签: android kotlin kotlin-coroutines android-runonuithread

我不了解Kotlin协程的工作方式。 我需要在异步线程上进行长时间的工作,并在Android应用程序的UI线程上获得结果。 有人可以给我一些例子吗? 例如

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

午餐model.getAllCountries()是否会异步,但最终如何将结果获取到UI线程?

4 个答案:

答案 0 :(得分:3)

好吧!除了@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块返回之后,我们又回到了主线程上。

答案 1 :(得分:2)

按照documentation for viewModelScope

  

此范围绑定到Dispatchers.Main.immediate

public void OnDominantSpeakerChanged(Room room, RemoteParticipant remoteParticipant) { if (remoteParticipant!=null) { if (remoteParticipant.RemoteVideoTracks.Count > 0) { RemoteVideoTrackPublication remoteVideoTrackPublication = remoteParticipant.RemoteVideoTracks[0]; remoteParticipantIdentity = remoteParticipant.Identity; AddRemoteParticipantVideo(remoteVideoTrackPublication.RemoteVideoTrack); } } } 是Kotlin说“主线程”的方式。这意味着默认情况下,Dispatchers.Main块中的所有代码都在主线程上运行。例如,您的launch要在另一个线程上运行,则想使用getAllCountries()移至IO协程分派器。

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

答案 2 :(得分:0)

  

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

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

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

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

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

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

答案 3 :(得分:0)

另一种解决方案是将结果发布到ViewModel类内的MutableLiveData中,并在视图中观察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)
            })
        }
    }