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
相关产品推荐
相关产品推荐

