如何在Jetpack Compose中通过Dagger/Hilt注入共享全局ViewModel实例?
在Jetpack Compose中通过Hilt共享全局ViewModel实例
你的问题核心在于:hiltViewModel()默认会将ViewModel与当前的NavBackStackEntry绑定,每个导航屏幕都会创建独立的ViewModel实例,导致无法共享状态。要实现全局共享,需要让ViewModel与Activity的生命周期绑定,以下是两种可行方案:
方案1:指定ViewModelStoreOwner为Activity
hiltViewModel()允许传入自定义的ViewModelStoreOwner,只需指定为当前Activity,就能拿到与Activity同生命周期的全局实例:
首先确保你的ViewModel添加了@HiltViewModel注解(Hilt注入的必要条件):
@HiltViewModel class MainNavigationViewModel @Inject constructor( // 这里注入你的依赖,比如Repository等 ) : ViewModel() { // 你的状态定义,比如StateFlow val state = MutableStateFlow(NavigationMeasurements()) }
然后在屏幕Composable中,通过LocalContext获取Activity实例并传入hiltViewModel():
@Composable fun ScreenA( navigationViewModel: MainNavigationViewModel = hiltViewModel( viewModelStoreOwner = LocalContext.current as MainActivity ) ) { val uiState by navigationViewModel.state.collectAsStateWithLifecycle() // 你的界面逻辑 }
注意:如果你的Activity继承自
AppCompatActivity,也可以强转为AppCompatActivity,只要确保是当前承载Compose的Activity实例即可。
方案2:使用CompositionLocal全局提供ViewModel
这种方式更简洁,适合全局频繁使用的ViewModel,避免重复传递参数:
- 先定义一个
CompositionLocal:
val LocalMainNavigationViewModel = compositionLocalOf<MainNavigationViewModel> { error("MainNavigationViewModel未在CompositionLocal中提供") }
- 在Activity的Composable中提供这个ViewModel:
@AndroidEntryPoint class MainActivity : BaseActivity() { override val viewModel: MainNavigationViewModel by viewModels() override val screen: @Composable () -> Unit = { CompositionLocalProvider(LocalMainNavigationViewModel provides viewModel) { MainNavigation() } } }
- 在任意子Composable中直接获取全局实例:
@Composable fun ScreenA( navigationViewModel: MainNavigationViewModel = LocalMainNavigationViewModel.current ) { val uiState by navigationViewModel.state.collectAsStateWithLifecycle() // 你的界面逻辑 }
关键注意事项
- 必须给ViewModel添加
@HiltViewModel注解,否则Hilt无法完成依赖注入并管理实例。 - 避免使用普通的
viewModel()函数,它不支持Hilt注入,且默认作用域也是当前Composable的ViewModelStoreOwner。 - 两种方案都能保证ViewModel与Activity同生命周期,只要Activity不销毁,状态就会持续共享,所有订阅状态的Composable都会自动重组。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

