Dagger Hilt父子Fragment场景下如何共享同作用域LoginInfo到各自ViewModel
解决方案
有两种常用实现方案,无需共用ViewModel,可直接实现父子Fragment各自ViewModel共享同生命周期的LoginInfo实例,两种方案的作用域都和父Fragment生命周期完全绑定,不会出现全局或Activity级别的内存泄漏风险:
方案1:复用父Fragment原生@FragmentScope(无自定义组件,更轻量)
利用Hilt原生Fragment组件的作用域特性,子Fragment主动从父Fragment的Component中获取作用域绑定的实例:
1.1 声明LoginInfo的绑定,指定@FragmentScope
@Module @InstallIn(FragmentComponent::class) object FragmentLoginModule { @Provides @FragmentScope fun provideLoginInfo(): LoginInfo = LoginInfo() }
1.2 定义EntryPoint用于从父Fragment组件获取实例
@EntryPoint @InstallIn(FragmentComponent::class) interface ParentFragmentLoginEntryPoint { fun getLoginInfo(): LoginInfo }
1.3 在父子Fragment中获取同一份LoginInfo注入到各自ViewModel
- 父Fragment侧:直接通过构造注入LoginInfo传给自己的ViewModel即可,此时获取的是父Fragment
@FragmentScope下的单例 - 子Fragment侧:先获取父Fragment实例,再通过EntryPoint拿到父Fragment作用域下的LoginInfo,传给自己的ViewModel:
// 子Fragment中获取父Fragment的LoginInfo实例 val parentFrag = generateSequence(parentFragment) { it.parentFragment } .filterIsInstance<你的父Fragment类型>() .firstOrNull() ?: error("未找到对应父Fragment") val loginInfo = EntryPointAccessors.fromFragment(parentFrag, ParentFragmentLoginEntryPoint::class.java) .getLoginInfo() // 后续将loginInfo传给子ViewModel即可
该方案优势是无需自定义Hilt组件,代码量少,适配大多数临时场景。
方案2:自定义@ParentFragmentScope作用域(更规范,适合多处复用该作用域的场景)
如果项目中多处需要父Fragment级别的作用域,可以自定义专属作用域,使用更规范:
2.1 自定义作用域注解
@Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class ParentFragmentScope
2.2 自定义Hilt组件与安装器
// 自定义组件,父级绑定原生FragmentComponent,作用域为我们自定义的@ParentFragmentScope @ParentFragmentScope @DefineComponent(parent = FragmentComponent::class) interface ParentFragmentComponent { @DefineComponent.Builder interface Builder { fun bindFragment(@BindsInstance fragment: Fragment): Builder fun build(): ParentFragmentComponent } } // 组件持有者,将组件实例存在父Fragment的Tag中,保证每个父Fragment仅创建一次组件 object ParentFragmentComponentHolder { fun get(fragment: Fragment): ParentFragmentComponent { val parentFrag = generateSequence(fragment) { it.parentFragment } .filterIsInstance<你的父Fragment类型>() .firstOrNull() ?: error("未找到对应父Fragment") return parentFrag.getTag(R.id.parent_fragment_component_key) as? ParentFragmentComponent ?: ParentFragmentComponent.builder().bindFragment(parentFrag).build().also { parentFrag.setTag(R.id.parent_fragment_component_key, it) } } }
注意需要在
res/values/ids.xml中添加<item name="parent_fragment_component_key" type="id"/>作为Tag的唯一key。
2.3 绑定LoginInfo到自定义作用域
@Module @InstallIn(ParentFragmentComponent::class) object ParentFragmentModule { @Provides @ParentFragmentScope fun provideLoginInfo(): LoginInfo = LoginInfo() }
2.4 注入使用
父子ViewModel中都可以直接通过自定义组件的EntryPoint获取LoginInfo,只要是同一个父Fragment下的实例都会是同一个对象。
内容的提问来源于stack exchange,提问作者warrenTaehoon
相关产品推荐
相关产品推荐

