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'