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

Jetpack Compose中ViewModel发起API调用出现无限循环求助

问题分析与解决方案

核心问题:ViewModel实例重复创建

你当前的ViewModel创建方式是直接通过LibraryViewModel()实例化——不管是在Composable参数默认值里还是内部声明,每次Composable重组都会生成新的ViewModel实例。而你的ViewModel在init块里调用了getLibrary(),这就导致每次重组都新建ViewModel、都触发API调用,形成无限循环。

另外,当loadLibrary状态变化时,会触发Composable重组,重组又新建ViewModel、再次执行init,进一步加剧循环。

修复步骤

1. 正确创建ViewModel实例

在Compose中,必须使用viewModel()函数(来自androidx.lifecycle:lifecycle-viewmodel-compose依赖)来获取ViewModel实例,它会确保同一个Composable树中只存在一个ViewModel实例:

@SuppressLint("StateFlowValueCalledInComposition")
@Destination
@Composable
fun HomeScreen(
    navigator: DestinationsNavigator,
    // 用viewModel()函数替代直接实例化
    libraryViewModel: LibraryViewModel = viewModel()
) {
    // ... 你的Composable代码
}

2. 优化ViewModel中的状态管理

当前ViewModel里的library是普通ArrayList,无法触发Compose重组;loadLibrary是mutableStateOf,但建议将library也改为可观察状态,确保数据更新能正确驱动UI:

方案一:使用mutableStateListOf

class LibraryViewModel() : ViewModel() {
    // 改为mutableStateListOf,支持自动触发重组
    val library = mutableStateListOf<PKIssue>()
    var loadLibrary by mutableStateOf(false)

    init {
        getLibrary()
    }

    fun getLibrary(){
        viewModelScope.launch {
            Press.issues(
                result = object : result<ArrayList<Issue>, Error> {
                    override fun succeed(result: ArrayList<Issue>?) {
                        loadLibrary = true
                        if (result != null) {
                            // 清空旧数据并添加新数据,避免直接赋值替换列表
                            library.clear()
                            library.addAll(result)
                        }
                    }

                    override fun failed(error: Error?) {
                        loadLibrary = false
                    }
                })
        }
    }
}

方案二:使用StateFlow(推荐,更适合复杂状态)

class LibraryViewModel() : ViewModel() {
    private val _library = MutableStateFlow<List<PKIssue>>(emptyList())
    val library: StateFlow<List<PKIssue>> = _library

    private val _loadLibrary = MutableStateFlow(false)
    val loadLibrary: StateFlow<Boolean> = _loadLibrary

    init {
        getLibrary()
    }

    fun getLibrary(){
        viewModelScope.launch {
            Press.issues(
                result = object : result<ArrayList<Issue>, Error> {
                    override fun succeed(result: ArrayList<Issue>?) {
                        _loadLibrary.value = true
                        result?.let {
                            _library.value = it
                        }
                    }

                    override fun failed(error: Error?) {
                        _loadLibrary.value = false
                    }
                })
        }
    }
}

在Composable中观察StateFlow:

@Composable
fun HomeScreen(
    navigator: DestinationsNavigator,
    libraryViewModel: LibraryViewModel = viewModel()
) {
    val library by libraryViewModel.library.collectAsState()
    val isLoading by libraryViewModel.loadLibrary.collectAsState()

    // ... 使用library和isLoading渲染UI
}

3. 确保依赖正确

确认项目中已添加ViewModel-Compose的依赖(build.gradle.kts):

dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
    // 其他必要依赖...
}

总结

  • 永远不要在Composable中直接new ViewModel(),必须使用viewModel()函数获取实例,避免重复创建。
  • 确保ViewModel中的状态使用Compose可观察的类型(mutableState*、StateFlow等),正确驱动UI重组而不会导致无限循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 22:20:41