首页 > 解决方案 > dagger2,我如何调用 SubComponent 的 @BindsInstance 表示方法..?

问题描述

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

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

我有一个 ApplicationComponent,它有一个 Builder,它的 @BindsInstance 看起来工作正常。我可以像下面这样使用

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

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

下面是代码片段

[应用组件]

@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
    }
}

[主要活动]

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 不会自动生成它。我正在寻找很多网站来解决这个问题,但都失败了。有什么办法可以做到吗?

标签: androiddagger-2dagger

解决方案


我想,我已经找到了实现你想要的方法。很抱歉没有使用您的特定示例,但更容易粘贴我拥有的代码并且知道可以在我自己的 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>
}

主要应用

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)
  }
}

运行时绑定活动

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范围控制的其他类,您需要手动管理此子组件的生命周期。将它存储在某个地方(可能在您的自定义应用程序类中),然后您还必须将其引用设置为null您的活动被销毁的时间,否则您将泄漏该活动。


推荐阅读