You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将viewModelScope中的异步结果传递至Activity及Compose页面?

ViewModelScope请求结果传递到Composable的问题解决

我尝试把viewModelScope中API请求的结果传递到Composable里,写了如下的CurrencyConverterViewModel:

class CurrencyConverterViewModel : ViewModel() {

    lateinit var response: CurrencyCodesModel
    var listOfNames : List<String> = emptyList()

    init {
        viewModelScope.launch {
            getListOfCurrencyNamesApi()
        }
    }

    suspend fun getCurrencyCodes() {
        response = CurrencyConverterInstance
            .currencyApiService
            .getCurrencyCodes()
    }
    private suspend fun getListOfCurrencyNamesApi() {

        getCurrencyCodes()

        listOfNames = response.supported_codes.map { it[1] }
    }
}

其中getCurrencyCodes负责调用API并存储返回结果,getListOfCurrencyNamesApi从返回结果中提取数据转换成字符串列表。

但在Composable中创建ViewModel实例后,尽管Logcat显示listOfNames已有数据,把它赋值给currencyListOfNames.value时却始终是空的:

@Composable
fun CurrencyConverter() {

    val myViewModel: CurrencyConverterViewModel = viewModel()
    val currencyListOfNames = remember { mutableStateOf<List<String>>(emptyList()) }

    currencyListOfNames.value = myViewModel.listOfNames
}

问题原因

  1. 首次Composable组合时,ViewModel里的协程还在执行API请求,listOfNames还是初始的空列表,此时赋值自然拿不到数据。
  2. 协程执行完更新listOfNames后,因为它只是普通变量,Composable无法感知到数据变化,不会触发重组去更新currencyListOfNames。

解决方案:使用可观察状态容器

要让Composable能感知到ViewModel中数据的变化,需要用StateFlow或LiveData这类可观察的状态容器。

方案一:StateFlow(推荐用于Jetpack Compose)

修改ViewModel,把listOfNames改成MutableStateFlow:

class CurrencyConverterViewModel : ViewModel() {

    lateinit var response: CurrencyCodesModel
    // 内部用MutableStateFlow存储数据,初始为空列表
    private val _listOfNames = MutableStateFlow<List<String>>(emptyList())
    // 对外暴露不可变的StateFlow,避免外部直接修改数据
    val listOfNames = _listOfNames.asStateFlow()

    init {
        viewModelScope.launch {
            getListOfCurrencyNamesApi()
        }
    }

    suspend fun getCurrencyCodes() {
        response = CurrencyConverterInstance
            .currencyApiService
            .getCurrencyCodes()
    }

    private suspend fun getListOfCurrencyNamesApi() {
        getCurrencyCodes()
        // 更新StateFlow的值,自动通知订阅者
        _listOfNames.value = response.supported_codes.map { it[1] }
    }
}

在Composable中用collectAsState订阅StateFlow,数据变化时自动触发重组:

@Composable
fun CurrencyConverter() {
    val myViewModel: CurrencyConverterViewModel = viewModel()
    // 自动收集StateFlow的数据,默认值为空列表
    val currencyListOfNames by myViewModel.listOfNames.collectAsState()

    // 示例:用LazyColumn展示列表
    LazyColumn {
        items(currencyListOfNames) { currencyName ->
            Text(
                text = currencyName,
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

方案二:LiveData(如果习惯使用LiveData)

修改ViewModel使用LiveData:

class CurrencyConverterViewModel : ViewModel() {

    lateinit var response: CurrencyCodesModel
    private val _listOfNames = MutableLiveData<List<String>>(emptyList())
    val listOfNames: LiveData<List<String>> = _listOfNames

    init {
        viewModelScope.launch {
            getListOfCurrencyNamesApi()
        }
    }

    suspend fun getCurrencyCodes() {
        response = CurrencyConverterInstance
            .currencyApiService
            .getCurrencyCodes()
    }

    private suspend fun getListOfCurrencyNamesApi() {
        getCurrencyCodes()
        _listOfNames.value = response.supported_codes.map { it[1] }
    }
}

在Composable中用observeAsState观察LiveData:

@Composable
fun CurrencyConverter() {
    val myViewModel: CurrencyConverterViewModel = viewModel()
    // 将LiveData转换为Compose状态,默认值为空列表
    val currencyListOfNames by myViewModel.listOfNames.observeAsState(emptyList())

    // 展示列表示例
    LazyColumn {
        items(currencyListOfNames) { currencyName ->
            Text(
                text = currencyName,
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

内容的提问来源于stack exchange,提问作者Sebastian Litwiniuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:52:15