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

如何在不同ViewModel间正确共享ViewModel Delegate及Hilt配置咨询

这种共享ViewModel功能的方式是否合理?Hilt配置及替代方案

一、方式合理性分析

这种用委托抽离共享ViewModel逻辑的方式是合理的,核心优势在于:

  • 把筛选相关的状态、交互逻辑从两个页面的ViewModel中抽离,符合单一职责原则,避免重复代码。
  • 通过Kotlin委托特性,让两个页面ViewModel自然拥有筛选功能,调用方无需额外适配逻辑。

但当前实现有两个潜在问题需要修正:

  1. initFilterViewModelDelegate会被两个ViewModel分别调用,导致重复执行初始化逻辑(比如重复请求筛选项),需要加判断避免重复执行。
  2. 依赖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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:00:42