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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 22:54:02