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生命周期绑定,步骤如下:
- 添加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"
- 保留原
ProductUseCaseModule的FragmentComponent作用域:
@Module @InstallIn(FragmentComponent::class) object ProductUseCaseModule { @Provides fun provideGetProductUseCase(repo: ProductRepository): GetProductUseCase { return GetProductUseCaseDefault(repo) } }
- 用
@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] } } }
- 在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
相关产品推荐
相关产品推荐

