dagger2,我如何调用@BindsInstance表示SubComponent的方法..?

时间:2018-06-13 09:23:20

标签: android dagger-2 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
    }
}

[ApplicationModule]

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

}

[MainComponent]

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

1 个答案:

答案 0 :(得分:2)

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

非常重要要注意,以这种方式生成的子组件并没有被匕首神奇地存储在某处。如果您希望后期绑定实例可用于注入由@PerActivity范围控制的其他类,则需要手动管理此子组件的生命周期。将它存储在某个地方(可能在您的自定义应用程序类中),然后您还必须在活动被销毁时将其引用设置为null,否则您将泄露该活动。