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

Android Compose如何正确设置Activity Observable传递ViewModel API数据到列表Data类

问题修正与实现方案

1. 先修复ViewModel的已知问题

  • 笔误修正:你声明的LiveData变量是movieList,但请求成功后调用的是dealList.postValue(items),会导致数据无法正常更新到LiveData,修改为一致的变量名即可。
  • 协程写法优化:不需要嵌套两层协程,viewModelScope可以直接指定IO调度器,且自带ViewModel生命周期绑定,不需要自己维护Job和取消逻辑,优化后代码如下:
class DealsViewModel(val repository: MainRepository) : ViewModel() {
    val movieList = MutableLiveData<List<DealItems>>()
    val loading = MutableLiveData<Boolean>()
    val errorMessage = MutableLiveData<String>()
    private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        onError("Exception handled: ${throwable.localizedMessage}")
    }

    fun getMovies()  {
        loading.value = true // 请求开始前先设置加载状态为true
        viewModelScope.launch(Dispatchers.IO + exceptionHandler) {
            val items = repository.getProduct()
            withContext(Dispatchers.Main) {
                loading.value = false
                if (items.isNullOrEmpty()) {
                    // 补充空数据逻辑
                } else {
                    movieList.postValue(items)
                }
            }
        }
    }

    private fun onError(message: String) {
        errorMessage.postValue(message)
        loading.postValue(false)
    }
}

2. getMovies()的调用位置

直接在onCreate方法中,ViewModel初始化完成后调用即可,也就是你原来注释掉的位置,修改为viewModel.getMovies(),这样页面首次打开就会自动发起请求。

3. LiveData传递给Composable的实现方式

Jetpack Compose提供了observeAsState()扩展方法,可以直接把LiveData转换成Compose可观察的State对象,不需要在Activity里手动写observe回调,优化后的Activity代码如下:

class MainActivity: ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val retrofitService = RetrofitService.getInstance()
        val viewModel = ViewModelProvider(
            this,
            MyViewModelFactory(MainRepository(retrofitService = retrofitService))
        ).get(DealsViewModel::class.java)
        // 页面首次加载发起请求
        viewModel.getMovies()

        // 错误提示监听保留即可
        viewModel.errorMessage.observe(this) {
            Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
        }

        setContent {
            myApp {
                // 把LiveData转成Compose可响应的State
                val movieList by viewModel.movieList.observeAsState(emptyList())
                val isLoading by viewModel.loading.observeAsState(false)
                // 直接把数据作为参数传给Composable函数
                MyScreenContent(
                    movieList = movieList,
                    isLoading = isLoading
                )
            }
        }
    }
}

// 对应的MyScreenContent函数接收参数示例
@Composable
fun MyScreenContent(
    movieList: List<DealItems>,
    isLoading: Boolean
) {
    if (isLoading) {
        // 加载中UI,比如居中的圆形进度条
        CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
    } else {
        // 列表UI,用LazyColumn渲染电影列表
        LazyColumn {
            items(movieList) { movie ->
                // 渲染每个item的UI,直接取用movie的属性即可
                Text(text = movie.title)
                Text(text = movie.regularPrice.toString())
            }
        }
    }
}

额外说明

  • 你原来在setContent里写的observe回调不需要保留,observeAsState已经自动做了生命周期绑定,不会出现内存泄漏
  • 加载状态的UI可以直接在Composable里根据isLoading参数渲染,不需要在Activity的observe回调中处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 14:45:05