如何在不同ViewModel间正确共享ViewModel Delegate及Hilt配置咨询
这种共享ViewModel功能的方式是否合理?Hilt配置及替代方案
一、方式合理性分析
这种用委托抽离共享ViewModel逻辑的方式是合理的,核心优势在于:
- 把筛选相关的状态、交互逻辑从两个页面的ViewModel中抽离,符合单一职责原则,避免重复代码。
- 通过Kotlin委托特性,让两个页面ViewModel自然拥有筛选功能,调用方无需额外适配逻辑。
但当前实现有两个潜在问题需要修正:
initFilterViewModelDelegate会被两个ViewModel分别调用,导致重复执行初始化逻辑(比如重复请求筛选项),需要加判断避免重复执行。- 依赖
lateinit变量存在初始化风险,建议增加初始化状态判断,防止未初始化就使用的异常。
二、Dagger Hilt配置步骤
要让FilterViewModelDelegate在导航图范围内共享实例,需要配置Hilt的导航图作用域:
1. 确保导航依赖到位
在build.gradle中添加Hilt导航相关依赖(根据你用Compose还是View系统选择):
// Compose项目 implementation "androidx.hilt:hilt-navigation-compose:1.1.0" // View系统(Fragment) implementation "androidx.hilt:hilt-navigation-fragment:1.1.0"
2. 创建Hilt模块绑定Delegate
创建模块,将FilterViewModelDelegateImpl绑定到接口,并指定导航图作用域(@NavGraphScoped),确保同一个导航图内的ViewModel拿到同一个Delegate实例:
@Module @InstallIn(ViewModelComponent::class) abstract class FilterDelegateModule { @Binds @NavGraphScoped abstract fun bindFilterViewModelDelegate( impl: FilterViewModelDelegateImpl ): FilterViewModelDelegate }
3. 修复Delegate的初始化问题
修改FilterViewModelDelegateImpl,增加初始化状态判断,避免重复执行初始化逻辑:
class FilterViewModelDelegateImpl : FilterViewModelDelegate { private var isInitialized = false private lateinit var savedStateHandle: SavedStateHandle private lateinit var viewModelScope: CoroutineScope private val _filterItems = MutableStateFlow<List<FilterItem>>(emptyList()) override val filterItems = _filterItems.asStateFlow() override fun initFilterViewModelDelegate( savedStateHandle: SavedStateHandle, viewModelScope: CoroutineScope ) { if (isInitialized) return this.savedStateHandle = savedStateHandle this.viewModelScope = viewModelScope // 仅执行一次的初始化逻辑,比如请求筛选项 fetchFilterItems() isInitialized = true } private fun fetchFilterItems() { viewModelScope.launch { // 替换为实际的接口请求逻辑 val items = emptyList<FilterItem>() _filterItems.emit(items) } } override fun onFilterItemClicked(filterItemId: String) { // 处理筛选项点击逻辑 } }
4. 确保ViewModel的作用域绑定
在获取页面ViewModel时,要绑定到目标导航图:
- Compose中:
val viewModel = hiltViewModel<FirstViewModel>()(默认绑定当前导航图) - View系统中:
val viewModel by hiltNavGraphViewModels<FirstViewModel>(R.id.your_nav_graph_id)
三、推荐替代方案:导航图级别共享ViewModel
如果觉得委托方式略显繁琐,更直接的方案是创建导航图级别的共享ViewModel,让页面ViewModel直接依赖它:
1. 创建共享的筛选ViewModel
@HiltViewModel @NavGraphScoped class FilterSharedViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val _filterItems = MutableStateFlow<List<FilterItem>>(emptyList()) val filterItems = _filterItems.asStateFlow() init { fetchFilterItems() } private fun fetchFilterItems() { viewModelScope.launch { // 筛选项请求逻辑 } } fun onFilterItemClicked(filterItemId: String) { // 筛选项点击处理 } }
2. 在页面ViewModel中依赖共享ViewModel
@HiltViewModel class FirstViewModel @Inject constructor( private val filterSharedViewModel: FilterSharedViewModel ) : ViewModel() { // 直接复用共享ViewModel的状态和方法 val filterItems = filterSharedViewModel.filterItems fun onFilterClicked(id: String) { filterSharedViewModel.onFilterItemClicked(id) } }
这种方案更贴合Jetpack ViewModel的设计思路,状态和逻辑都封装在共享ViewModel中,页面ViewModel只需依赖调用即可,无需额外委托逻辑。
内容的提问来源于stack exchange,提问作者Augusto Carmo
相关产品推荐
相关产品推荐

