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

未使用Hilt时,Jetpack Compose无法创建MainViewModel实例求助

问题描述

我正在使用Jetpack Compose开发Android应用,运行时出现如下错误:

Process: com.example.bangkit_recycleview, PID: 7393
java.lang.RuntimeException: Cannot create an instance of class com.example.pokedex.MainViewModel

我没有使用Hilt,只想单纯用Jetpack Compose,却碰到这个问题。以下是代码细节:

MainActivityScreen 代码

@Composable
fun MainActivityScreen(viewModel: MainViewModel = viewModel()) {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    val foods = viewModel.foods

    LaunchedEffect(key1 = context) {
        viewModel.getAllFoods()
    }
}

MainViewModel 代码

class MainViewModel(private val foodDao: FoodDao) : ViewModel() {
    private var _foods: List<FoodEntity> = emptyList()

    val foods: List<FoodEntity>
        get() = _foods

    fun getAllFoods() {
        viewModelScope.launch {
            _foods = withContext(Dispatchers.IO) {
                foodDao.getAllFoods()
            }
        }
    }

    fun insertFood(newFood: FoodEntity) {
        viewModelScope.launch {
            withContext(Dispatchers.IO) {
                foodDao.insert(newFood)
            }
        }
    }
}
解决方案

这个错误的核心原因是:Compose默认的viewModel()函数只能创建无参构造函数的ViewModel,而你的MainViewModel依赖FoodDao参数,系统无法自动实例化它。

不用Hilt的话,有两种直接的解决方式:

方式一:手动创建ViewModel并传入

在Activity中先获取FoodDao实例,创建好ViewModel后再传递给Compose组件:

1. 在Activity中完成ViewModel初始化

假设你用Room数据库,先拿到数据库实例:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 获取你的Room数据库实例
        val db = AppDatabase.getInstance(this)
        // 创建ViewModel并传入FoodDao
        val viewModel = MainViewModel(db.foodDao())
        
        setContent {
            YourAppTheme {
                MainActivityScreen(viewModel = viewModel)
            }
        }
    }
}

2. 修改Compose组件的参数定义

去掉默认的viewModel()调用,让外部传入ViewModel:

@Composable
fun MainActivityScreen(viewModel: MainViewModel) {
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    val foods = viewModel.foods

    LaunchedEffect(key1 = context) {
        viewModel.getAllFoods()
    }
}

方式二:自定义ViewModelFactory实现自动注入

如果想继续用viewModel()函数自动创建ViewModel,可以自定义Factory告诉系统如何实例化:

1. 实现ViewModelFactory类

class MainViewModelFactory(private val foodDao: FoodDao) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return MainViewModel(foodDao) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

2. 在Compose中使用Factory

修改MainActivityScreen的默认参数,传入自定义Factory:

@Composable
fun MainActivityScreen(viewModel: MainViewModel = viewModel(
    factory = MainViewModelFactory(getFoodDao())
)) {
    // 原有代码不变
}

// 辅助函数:从Context获取FoodDao
@Composable
private fun getFoodDao(): FoodDao {
    val context = LocalContext.current
    return AppDatabase.getInstance(context).foodDao()
}

额外优化:让数据更新触发Compose重组

你的ViewModel中_foods是普通List,数据更新后Compose不会自动重组UI,建议改成mutableStateListOf:

class MainViewModel(private val foodDao: FoodDao) : ViewModel() {
    private val _foods = mutableStateListOf<FoodEntity>()
    val foods: List<FoodEntity>
        get() = _foods

    fun getAllFoods() {
        viewModelScope.launch {
            val result = withContext(Dispatchers.IO) {
                foodDao.getAllFoods()
            }
            _foods.clear()
            _foods.addAll(result)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:52:38