Jetpack Compose中注入ViewModel导致UI预览渲染失败问题
我使用Jetpack Compose开发项目,采用Koin实现依赖注入。在@Composable函数中注入ViewModel并观察数据时,UI预览无法正常渲染,抛出以下异常:
java.lang.IllegalStateException: KoinApplication has not been started
at org.koin.core.context.GlobalContext.get(GlobalContext.kt:36)
at org.koin.compose.KoinApplicationKt.getKoinContext(KoinApplication.kt:52)
at org.koin.compose.KoinApplicationKt.access$getKoinContext(KoinApplication.kt:1)
at org.koin.compose.KoinApplicationKt.getKoinScope(KoinApplication.kt:86)
我已在Application类中完成Koin初始化,并在Manifest中配置了该类:
class MainApplication : Application() { override fun onCreate() { super.onCreate() startKoin { androidLogger() androidContext(this@MainApplication) modules(ApiModule) modules(appModule) } } }
Manifest配置:
<application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.ComposeRetrofit" android:name=".MainApplication" tools:targetApi="31">
Compose代码:
@Preview @Composable fun ViewForMainScreen(){ MainScreen() } @Composable fun getApplianceViewModel(): ApplianceViewModel { return koinViewModel() } @Composable fun getAddressViewModel(): AddressViewModel { return koinViewModel() } @Composable fun MainScreen(){ val addressViewModel = getAddressViewModel() val address by addressViewModel.addressApiResponse.observeAsState(initial = null) Column( modifier = Modifier .fillMaxWidth() .height(100.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Button( onClick = { addressViewModel.callAddressApi() }, modifier = Modifier .fillMaxWidth() .height(40.dp) .padding(start = 40.dp, end = 40.dp) ) { Text(text = address?.mailBox ?: "Country") } Spacer(modifier = Modifier.height(20.dp)) Text( text = address?.fullAddress ?: "Full Address", modifier = Modifier.padding(start = 40.dp, end = 40.dp) ) } }
问题原因
- Compose预览的运行环境是独立沙箱,不会启动你配置的
MainApplication类,因此不会执行其中的Koin初始化逻辑,导致预览时Koin全局上下文未创建。 - 预览系统仅加载Compose UI组件本身,不会触发Application的生命周期回调。
解决方法
方法一:在预览函数中手动初始化Koin
修改预览函数,用KoinApplication包裹待预览的Compose内容,手动初始化Koin模块:
@Preview @Composable fun ViewForMainScreen(){ KoinApplication(application = { androidLogger() androidContext(LocalContext.current) modules(ApiModule, appModule) }) { MainScreen() } }
方法二:使用模拟ViewModel替代依赖注入
为ViewModel创建模拟实现,在预览中直接传入模拟实例,避免依赖Koin:
// 模拟AddressViewModel class MockAddressViewModel : AddressViewModel() { override val addressApiResponse = MutableStateFlow(Address( mailBox = "Mock Country", fullAddress = "Mock Full Address" )) } @Preview @Composable fun ViewForMainScreen(){ val mockViewModel = MockAddressViewModel() val address by mockViewModel.addressApiResponse.observeAsState() Column( modifier = Modifier .fillMaxWidth() .height(100.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Button( onClick = {}, modifier = Modifier .fillMaxWidth() .height(40.dp) .padding(start = 40.dp, end = 40.dp) ) { Text(text = address?.mailBox ?: "Country") } Spacer(modifier = Modifier.height(20.dp)) Text( text = address?.fullAddress ?: "Full Address", modifier = Modifier.padding(start = 40.dp, end = 40.dp) ) } }
方法三:升级Koin到最新版本
最新版的Koin Compose库已针对预览场景做了优化,升级后可能无需额外配置即可自动处理预览时的Koin上下文初始化。
内容的提问来源于stack exchange,提问作者Sanjay

