Dagger2,我如何调用@BindsInstance标注的SubComponent方法?

3

请帮帮我!我在使用Dagger 2时遇到了麻烦。

我想在MainActivity中使用@Subcomponent.Builder@BindsInstance,在运行时而不是编译时绑定一些依赖项。

我有一个ApplicationComponent,它有一个Builder,在使用@BindsInstance时没有问题。我可以像下面这样使用:

DaggerApplicationComponent
    .builder()
    .application(this)
    .build()
    .inject(this)

但是一些麻烦来自MainActivity...

以下是代码片段

[ApplicationComponent]

@Singleton
@Component(modules = [ApplicationModule::class])
internal interface ApplicationComponent : AndroidInjector<MyApplication> {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder
        fun build(): ApplicationComponent
    }
}

[应用程序模块]
@Module(
        includes = [
            AndroidInjectionModule::class,
            AndroidSupportInjectionModule::class,
            ActivityInjectionModule::class
        ],
        subcomponents = [
            MainComponent::class
        ]
)
internal abstract class ApplicationModule {

    @PerActivity
    @ContributesAndroidInjector(modules = [SplashModule::class])
    abstract fun splashActivity(): SplashActivity

    @Binds
    @IntoMap
    @ActivityKey(MainActivity::class)
    abstract fun mainActivity(builder: MainComponent.Builder): AndroidInjector.Factory<out Activity>

}

[主要组件]

@PerActivity
@Subcomponent(modules = [MainModule::class])
internal interface MainComponent : AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    abstract class Builder : AndroidInjector.Builder<MainActivity>()  {
        @BindsInstance
        abstract fun testClass(mainTestClass: MainTestClass): Builder
    }
}

[MainActivity]

internal class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
//        This works find without runtime injection
//        AndroidInjection.inject(this)

        /**
         *I want to bind some dependency(in this case, MainTestClass) in runtime like below.
         * so that I can use MainTestClass inside MainModule to inject this to other classes.
         * but, for some reason,
         * DaggerMainComponent IS NOT GENERATED AUTOMATICALLY...
         */
        DaggerMainComponent.builder()
                .testClass(MainTestClass())
                .build()
                .inject(this);

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startActivity(Intent(this, SplashActivity::class.java))
    }

}

问题在于我无法访问DaggerMainComponent,因为Dagger没有自动生成它。我浏览了很多网站来解决这个问题,但都失败了。有没有什么方法可以解决呢?


我有一个包含有效示例的教程:https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj - AutonomousApps
@AutonomousApps 我已经仔细阅读了您的帖子。它似乎描述了如何将Context传递给MainApplicationComponent,但这是Component而不是Subcomponent。我正在寻找一种将某些依赖项传递给Subcomponent builder的方法。无论如何,感谢您的评论! - J.ty
是的,你说得对。我在完全阅读你的帖子之前就发表了我的评论。实际上,我一直在计划撰写一篇博客文章来讨论你所使用的案例,但还没有时间去做。在这里,我只能草拟一个解决方案,但请考虑你的DaggerMainComponent是一个SUBcomponent,Dagger必须自己提供它。您实际上可以注入子组件生成器!这样做,并使用注入的生成器来完成您想要的事情。我将尝试在本周末编写一个完整的解决方案,但如果您尝试并且成功,请告诉我。 - AutonomousApps
你能更具体地说明你的用例吗?MainTestClass是什么?为什么需要在运行时而不是编译时确定它?如果它与测试有关,正如情况所示,为什么不将其绑定到根组件? - AutonomousApps
@AutonomousApps MainTestClass只是简单的POJO具体类,请不要在意类名。它与单元测试无关。实际上,在我编写的这个示例代码中,你可能认为没有特定的理由使用运行时绑定。没错,但是,在另一个我的真实应用程序代码中,有以上情况的需求。因此,我想知道是否有任何方法在运行时进行绑定,以上样本代码行是否可行。 - J.ty
感谢澄清这与测试无关。然而,更好地了解您需要此功能的原因将有助于我帮助您。您不能使用AndroidInjection来注入具有BindsInstance方法的子组件,因为Dagger会抱怨您的POJO未提供且为空。但是,您可以在非框架类上使用构造函数注入以注入子组件构建器,构建子组件,然后将其存储在某个位置以注入您的活动。或者您可以通过意图传递一些内容。 - AutonomousApps
1个回答

3

我想我已经找到了实现你所需的方法。很抱歉没有使用你的具体示例,但将我自己 IDE 中已知可行的代码粘贴进来更容易。我已经在关键行添加了注释。以下是代码:

单例组件

@Singleton
@Component(modules = [
  AndroidSupportInjectionModule::class,
  RuntimeBindingModule::class // my addition!
])
interface MainApplicationComponent {

  fun inject(app: MainApplication)

  // my addition!
  fun runtimeBuilder(): RuntimeBindingActivitySubcomponent.Builder

  @Component.Builder
  interface Builder {
    fun build(): MainApplicationComponent
    @BindsInstance fun app(app: Context): Builder
  }
}

这个绑定代码与你的基本相同。

@Subcomponent
interface RuntimeBindingSubcomponent : AndroidInjector<RuntimeBindingActivity> {
  @Subcomponent.Builder
  abstract class Builder : AndroidInjector.Builder<RuntimeBindingActivity>() {
    @BindsInstance abstract fun bindInt(intVal: Int): Builder
  }
}

@Module(subcomponents = [RuntimeBindingSubcomponent::class])
abstract class RuntimeBindingActivityModule {
  @Binds @IntoMap @ActivityKey(RuntimeBindingActivity::class)
  abstract fun bindInjectorFactory(
    builder: RuntimeBindingActivitySubcomponent.Builder
  ): AndroidInjector.Factory<out Activity>
}

MainApplication

open class MainApplication : Application(), HasActivityInjector {

  // This needs to be accessible to your Activities
  lateinit var component: MainApplication.MainApplicationComponent

  override fun onCreate() {
    super.onCreate()
    initDagger()
  }

  private fun initDagger() {
    component = DaggerMainApplicationComponent.builder()
      .app(this)
      .build()
    component.inject(this)
  }
}

RuntimeBindingActivity

class RuntimeBindingActivity : AppCompatActivity() {

  // I had to use @set:Inject because this is a primitive and we can't use lateinit 
  // on primitives. But for your case, 
  // `@Inject lateinit var mainTestClass: MainTestClass` would be fine
  @set:Inject var intVal: Int = -1

  override fun onCreate(savedInstanceState: Bundle?) {
    // And this is how you can get runtime binding
    val subComponent = (application as MainApplication).component.runtimeBuilder()
    with(subComponent) {
      seedInstance(this@RuntimeBindingActivity)
      bindInt(10) // runtime binding
      build()
    }.inject(this)

    Log.d("RuntimeBindingActivity", "intVal = $intVal")

    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_runtime_binding)
  }
}

重要提示:需要注意的是,通过这种方式生成的子组件并没有被 Dagger 自动存储在某个地方。如果您希望将后期绑定实例用于注入到由 @PerActivity 作用域控制的其他类中,您需要手动管理此子组件的生命周期。将其存储在某个位置(可能是您的自定义 Application 类),然后在活动销毁时还必须将其引用设置为 null,否则会泄漏该活动。

谢谢您的回复。我稍后会尝试您的练习。我认为它可能有效,但正如您所提到的,看起来我应该管理子组件的生命周期。因为您的帮助,我对Dagger 2有了更多的了解。感谢! - J.ty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接