Kotlin 协程列表返回空值
Kotlin coroutine list returning null value
android 应用开发的新手。我正在使用 kotlin,我正在尝试从我的视图模型中的房间数据库中检索列表,并在我按下按钮时为我的片段干杯(下面的代码)。如果我按一次按钮,我得到一个空字符串,但如果我按两次,我得到列表。我该怎么做才能一键检索列表?可能我从协程中遗漏了一些东西。
查看模型代码:
var Listado = ""
fun listaTotal(): String {
uiScope.launch {
getTodaListaCompra().forEach{
Log.i("Listado Compra",Listado )
Listado = Listado + " " + it
Log.i("Data",data.value)
Log.i("Pueba",it)
}
}
return Listado
}
片段调用:
Toast.makeText(application, tabListaCompraViewModel.listaTotal(), Toast.LENGTH_SHORT)
.show()
提前致谢
您在开始时定义了一个空字符串。第一次调用 listaTotal() 时,会在后台启动一个协程来计算 'listado' 的值。但是 listaTotal 的 return 并没有等待后台协程完成。这就是为什么 'listado' 仍然是空的。
在你第一次和第二次点击按钮之间,第一个协程完成并且 'listado' 现在不再是空的,所以当你第二次点击按钮时,协程再次启动但是 'listado',在协程完成之前再次得到 returned,因此它 return 是第一次单击按钮的结果。
因为只能在主UI线程上做Toast,所以需要告诉它等待协程完成才能得到returned值。您可以使用 runBlocking 来完成,如下所示:
fun listaTotal(): String = runBlocking {
getTodaListaCompra().forEach{
Log.i("Listado Compra",listado )
listado += " " + it
Log.i("Data",data.value)
Log.i("Pueba",it)
}
listado
}
更新:
澄清一下,这种方法会阻塞主 UI 线程,直到结果被 returned。
因此,您应该考虑使用 LiveData(参见 Sergeys 的回答)或 Flows 来获取数据。这个答案的目的主要是解释你的代码和协程的一般行为。
我建议使用LivaData观察数据:
class MyViewModel : ViewModel() {
val listado: LiveData<String> = MutableLiveData<String>()
fun listaTotal() = viewModelScope.launch {
var localListado = ""
getTodaListaCompra().forEach{
localListado = "$localListado $it"
}
(listado as MutableLiveData).value = localListado
}
// function marked as suspend to suspend a coroutine without blocking the Main Thread
private suspend fun getTodaListaCompra(): List<String> {
delay(1000) // simulate request delay
return listOf("one", "two", "three")
}
}
在您的 activity 或片段中,您可以使用下一个方法来实例化 ViewModel class 并观察数据:
private fun initViewModel() {
val viewModel = ViewModelProvider(
this,
viewModelFactory { MyViewModel() }
)[MyViewModel::class.java]
viewModel.listado.observe(this, androidx.lifecycle.Observer { data: String ->
Toast.makeText(application, data, Toast.LENGTH_SHORT).show()
})
viewModel.listaTotal()
}
inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(aClass: Class<T>):T = f() as T
}
您可能还需要导入下一个库:
api 'androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION'
android 应用开发的新手。我正在使用 kotlin,我正在尝试从我的视图模型中的房间数据库中检索列表,并在我按下按钮时为我的片段干杯(下面的代码)。如果我按一次按钮,我得到一个空字符串,但如果我按两次,我得到列表。我该怎么做才能一键检索列表?可能我从协程中遗漏了一些东西。
查看模型代码:
var Listado = ""
fun listaTotal(): String {
uiScope.launch {
getTodaListaCompra().forEach{
Log.i("Listado Compra",Listado )
Listado = Listado + " " + it
Log.i("Data",data.value)
Log.i("Pueba",it)
}
}
return Listado
}
片段调用:
Toast.makeText(application, tabListaCompraViewModel.listaTotal(), Toast.LENGTH_SHORT)
.show()
提前致谢
您在开始时定义了一个空字符串。第一次调用 listaTotal() 时,会在后台启动一个协程来计算 'listado' 的值。但是 listaTotal 的 return 并没有等待后台协程完成。这就是为什么 'listado' 仍然是空的。
在你第一次和第二次点击按钮之间,第一个协程完成并且 'listado' 现在不再是空的,所以当你第二次点击按钮时,协程再次启动但是 'listado',在协程完成之前再次得到 returned,因此它 return 是第一次单击按钮的结果。
因为只能在主UI线程上做Toast,所以需要告诉它等待协程完成才能得到returned值。您可以使用 runBlocking 来完成,如下所示:
fun listaTotal(): String = runBlocking {
getTodaListaCompra().forEach{
Log.i("Listado Compra",listado )
listado += " " + it
Log.i("Data",data.value)
Log.i("Pueba",it)
}
listado
}
更新: 澄清一下,这种方法会阻塞主 UI 线程,直到结果被 returned。 因此,您应该考虑使用 LiveData(参见 Sergeys 的回答)或 Flows 来获取数据。这个答案的目的主要是解释你的代码和协程的一般行为。
我建议使用LivaData观察数据:
class MyViewModel : ViewModel() {
val listado: LiveData<String> = MutableLiveData<String>()
fun listaTotal() = viewModelScope.launch {
var localListado = ""
getTodaListaCompra().forEach{
localListado = "$localListado $it"
}
(listado as MutableLiveData).value = localListado
}
// function marked as suspend to suspend a coroutine without blocking the Main Thread
private suspend fun getTodaListaCompra(): List<String> {
delay(1000) // simulate request delay
return listOf("one", "two", "three")
}
}
在您的 activity 或片段中,您可以使用下一个方法来实例化 ViewModel class 并观察数据:
private fun initViewModel() {
val viewModel = ViewModelProvider(
this,
viewModelFactory { MyViewModel() }
)[MyViewModel::class.java]
viewModel.listado.observe(this, androidx.lifecycle.Observer { data: String ->
Toast.makeText(application, data, Toast.LENGTH_SHORT).show()
})
viewModel.listaTotal()
}
inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(aClass: Class<T>):T = f() as T
}
您可能还需要导入下一个库:
api 'androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION'