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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 17:06:03