如何在Jetpack Compose的Activity中调用suspend函数获取API数据展示
实现方案
方案1(推荐):ViewModel托管请求+状态观察
这种方式符合架构规范,请求生命周期和ViewModel绑定,避免重复请求和内存泄漏。
第一步:修改BookViewModel
把请求逻辑放到ViewModel内部执行,暴露可观察的状态给UI:
@HiltViewModel class BookViewModel @Inject constructor( private val repository: BookRepository ) : ViewModel() { // 定义UI状态密封类,也可以单独抽成文件 sealed class BookUiState { object Loading : BookUiState() data class Success(val bookList: BookList) : BookUiState() data class Error(val msg: String) : BookUiState() } private val _uiState = MutableStateFlow<BookUiState>(BookUiState.Loading) val uiState = _uiState.asStateFlow() init { // ViewModel初始化时就发起请求,也可以暴露方法给UI主动触发 fetchBookList() } private fun fetchBookList() = viewModelScope.launch { try { val bookList = repository.getBookList() if (bookList != null) { _uiState.value = BookUiState.Success(bookList) } else { _uiState.value = BookUiState.Error("获取书籍列表为空") } } catch (e: Exception) { _uiState.value = BookUiState.Error("请求失败: ${e.localizedMessage}") } } }
第二步:修改MainActivity的UI逻辑
在Compose中收集状态,根据不同状态渲染对应页面,首先确保你导入了生命周期相关依赖来安全收集Flow,模块build.gradle中添加依赖:implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
然后修改MainActivity代码:
@AndroidEntryPoint class MainActivity : ComponentActivity() { private val bookViewModel: BookViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { LOTRAppTheme { // 收集UI状态,和Activity生命周期绑定 val uiState by bookViewModel.uiState.collectAsStateWithLifecycle() when(uiState) { is BookViewModel.BookUiState.Loading -> { // 加载中状态,可自定义加载UI Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } is BookViewModel.BookUiState.Success -> { // 成功拿到数据,渲染列表页 bookListScreen(bookList = (uiState as BookViewModel.BookUiState.Success).bookList) } is BookViewModel.BookUiState.Error -> { // 错误状态,可自定义错误UI和重试逻辑 Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text(text = (uiState as BookViewModel.BookUiState.Error).msg) } } } } } } }
方案2:直接在Composable中调用suspend方法
如果你不需要复杂的状态管理,只想临时调用suspend方法,可以用LaunchedEffect提供的协程作用域执行请求,无需修改ViewModel,仅修改MainActivity的setContent块即可:
setContent { LOTRAppTheme { // 定义存储书籍列表的状态 var bookListState: BookList? by remember { mutableStateOf(null) } var isLoading by remember { mutableStateOf(true) } var errorMsg by remember { mutableStateOf<String?>(null) } // key为Unit表示仅在Composable第一次进入时执行请求 LaunchedEffect(Unit) { try { val result = bookViewModel.getBookList() if (result != null) { bookListState = result } else { errorMsg = "书籍列表为空" } } catch (e: Exception) { errorMsg = "请求失败: ${e.localizedMessage}" } finally { isLoading = false } } when { isLoading -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } errorMsg != null -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text(text = errorMsg!!) } } bookListState != null -> { bookListScreen(bookList = bookListState!!) } } } }
注意:这种方式的请求生命周期和Composable绑定,如果页面重组且LaunchedEffect的key发生变化,会重新执行请求,适合一次性请求的简单场景。
内容的提问来源于stack exchange,提问作者Hasan Altay
相关产品推荐
相关产品推荐

