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

BottomNavigation多Fragment实例下Hilt仓库注入ViewModel失败求助

问题解决指南

1. 解决Fragment实例重复问题

BottomNavigation默认会复用已添加的Fragment实例,导致标签1和2共用同一个ProductFragment。要避免这种情况,需每次切换标签时创建新的Fragment实例,并为每个实例设置唯一标识:

// 标签切换逻辑示例
private fun switchTab(position: Int) {
    val targetFragment = when(position) {
        0, 1 -> ProductFragment.newInstance() // 标签1、2都创建新的ProductFragment实例
        2 -> ThirdFragment.newInstance()
        3 -> FourthFragment.newInstance()
        else -> throw IllegalArgumentException("无效标签位置")
    }
    supportFragmentManager.beginTransaction()
        .replace(R.id.nav_container, targetFragment, "tab_fragment_$position") // 用位置作为唯一Tag
        .commit()
}

注意:不要使用add+hide/show的方式,这种方式会保留旧Fragment实例;改用replace确保每次替换为全新实例。


2. 实现Fragment作用域依赖注入到ViewModel

Hilt的@HiltViewModel默认绑定ViewModelComponent,该组件无法直接访问FragmentComponent的作用域依赖(ViewModel生命周期可能长于Fragment)。以下两种方案可解决此问题:

方案一:使用Assisted Inject实现跨组件注入

此方案可严格保证UseCase与Fragment生命周期绑定,步骤如下:

  1. 添加Assisted Inject依赖(在build.gradle中):
// 已有的Hilt依赖之外,补充以下依赖
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.7.0"
kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.7.0"
  1. 保留原ProductUseCaseModule的FragmentComponent作用域:
@Module
@InstallIn(FragmentComponent::class)
object ProductUseCaseModule {
    @Provides
    fun provideGetProductUseCase(repo: ProductRepository): GetProductUseCase {
        return GetProductUseCaseDefault(repo)
    }
}
  1. 用@AssistedInject改造ViewModel:
class ProductViewModel @AssistedInject constructor(
    private val getProductUseCase: GetProductUseCase
) : ViewModel() {

    // 定义工厂接口,由Hilt生成实现类
    @AssistedFactory
    interface Factory {
        fun create(): ProductViewModel
    }

    // 工具方法,简化Fragment创建ViewModel的逻辑
    companion object {
        fun getInstance(fragment: Fragment, factory: Factory): ProductViewModel {
            return ViewModelProvider(fragment, object : ViewModelProvider.Factory {
                override fun <T : ViewModel> create(modelClass: Class<T>): T {
                    return factory.create() as T
                }
            })[ProductViewModel::class.java]
        }
    }
}
  1. 在Fragment中注入工厂并创建ViewModel:
@AndroidEntryPoint
class ProductFragment : Fragment() {
    @Inject
    lateinit var viewModelFactory: ProductViewModel.Factory

    private val viewModel: ProductViewModel by lazy {
        ProductViewModel.getInstance(this, viewModelFactory)
    }

    // ... 业务代码
}

方案二:将UseCase移至ViewModelComponent(更简便)

若无需严格绑定UseCase与Fragment生命周期,仅需每个ViewModel拥有独立的UseCase,可直接修改Module的作用域:

@Module
@InstallIn(ViewModelComponent::class) // 修改作用域为ViewModelComponent
object ProductUseCaseModule {
    @Provides
    fun provideGetProductUseCase(repo: ProductRepository): GetProductUseCase {
        return GetProductUseCaseDefault(repo)
    }
}

ViewModel和Fragment保持原有写法即可——只要每个Fragment是新实例,by viewModels()会为每个Fragment创建独立的ViewModel,而ViewModelComponent的依赖会随ViewModel实例绑定,自然实现每个ViewModel拥有独立的UseCase。

此方案更简洁,推荐优先尝试。


内容的提问来源于stack exchange,提问作者Ramakrishna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 07:16:14