首页 > 解决方案 > Kotlin android - 将等待结果分配给变量

问题描述

tldr; 如何用异步数据初始化变量

学习 kotlin 和 android,bignerdranch 书中的简单示例。初学者问题在这里。

应用程序上显示一个数据类。

private var characterData = CharacterGenerator.generate()

这里 .generate() 只是得到一个静态元素。(用于显示的其他功能)

我实现了,所以现在点击按钮,数据是从 api 获取的(它有效)

generateButton.setOnClickListener {
    GlobalScope.launch(Dispatchers.Main) {
        characterData = fetchCharacterData().await()
        displayCharacterData()
    }
}

问题是如何实现初始数据获取并将其分配给 characterData ?

private var characterData = CharacterGenerator.generate() // we want to replace generate() with the api call results

显然这也行不通

private var characterData = fetchCharacterData().await()

但这也行不通

private var characterData = GlobalScope.launch(Dispatchers.Main) {
            characterData = fetchCharacterData().await()
            displayCharacterData()
        }

那么使用来自 api 的数据分配/初始化 charactedData 的正确方法是什么?

编辑:

fun fetchCharacterData(): Deferred<CharacterGenerator.CharacterData> {
    return GlobalScope.async {
        val apiData = URL(CHARACTER_DATA_API).readText()
        CharacterGenerator.fromApiData(apiData)
    }
}

fromApiData 返回 CharacterData 类型的 dataClass 实例

标签: androidkotlinkotlin-coroutines

解决方案


既然你不应该阻塞主线程,但字符数据最初是不可用的,你应该从空字符数据开始。我不知道你的字符数据类是什么样的,但你应该以一种允许它为空的方式组合它,或者只允许它为空。您的 UI 代码应该能够处理这种情况,只需不填写数据,或者最初显示 Spinner。

对于任何必须异步获取数据的应用程序,您都会一遍又一遍地看到这种模式。如果它非常快,则无需费心使用微调器。

旁注:避免使用 GlobalScope(文章由 Kotlin 的设计负责人撰写)。

第二个注意事项:有一个返回 Deferred 的函数有点笨拙。协程的标准模式是将其编写为挂起函数,并withContext在内部使用以确保在从 Main 调度程序调用时不会阻塞:

suspend fun fetchCharacterData(): CharacterGenerator.CharacterData = withContext(Dispatchers.IO) {
    val apiData = URL(CHARACTER_DATA_API).readText()
    CharacterGenerator.fromApiData(apiData)
}


generateButton.setOnClickListener {
    lifecycleScope.launch {
        characterData = fetchCharacterData()
        displayCharacterData()
    }
}

推荐阅读