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

