When using the ContributesAndroidInjector Annotation there's no way to provide the activity itself. In the rare cases I need this it's a pain to always use the "old" approach.
@Binds
@IntoMap
@ActivityKey(MainActivity::class)
internal abstract fun bindAndroidInjectorFactory(builder: MainActivitySubcomponent.Builder)
: AndroidInjector.Factory<out Activity>
@ActivityScope
@Subcomponent(modules = arrayOf(ActivityModule::class))
interface MainActivitySubcomponent : AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<MainActivity>() {
abstract fun module(module: ActivityModule): Builder
override fun seedInstance(instance: MainActivity) {
module(ActivityModule(instance))
}
}
}
with the module:
@Module
class ActivityModule(private val activity: AppCompatActivity) {
@Provides
@ActivityScope
fun provideActivity(): AppCompatActivity = activity
}
This is a lot of boilerplater why I love the annotation you introduced in 2.11
I would love to see an additional boolean(?) flag in this annotation(may be no IN this annotation but a different one?) which is default false, so that the activity is provided (the code for the subcomponent is generated automatically)
This could be figured out by reading the type of key (@ActivityKey) in the subcomponent
Given that AndroidInjector.Builder#seedInstance is annotated with @BindsInstance, shouldn't it already work? (it should already be equivalent to what you wrote, except for the @ActivityScope but is that really needed?)
It already works, yes, with the concrete type. And you can alias to the
general supertype with a @Binds.
On Wed, Nov 22, 2017, 10:41 AM Thomas Broyer notifications@github.com
wrote:
Given that AndroidInjector.Builder#seedInstance
https://google.github.io/dagger/api/2.13/dagger/android/AndroidInjector.Builder.html#seedInstance-T-
is annotated with @BindsInstance, shouldn't it already work? (it should
already be equivalent to what you wrote, except for the @ActivityScope
but is that really needed?)—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/google/dagger/issues/961#issuecomment-346388158, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAEEEZ5sC-sB1h4D1Mb4w4mhKlkOsXH6ks5s5EDAgaJpZM4QnjuP
.
Thanks
@Module
abstract class ApplicationModule {
@ContributesAndroidInjector
abstract fun provideActivity(): MainActivity
@Binds
abstract fun provideActivity(mainActivity: MainActivity): AppCompatActivity
}
One additional question:
What if I have 2 activities (lets say MainActivity and WhateverActivity) which both need to provide an AppCompatActivity?
I tried this and I am (of course) running into the issue that AppCompatActivity is mutliple times bound.
Any ideas on how to solve this?
The bindings for MainActivity and WhateverActivity should be in different components so it shouldn't be an issue, right?
@ronshapiro Indeed, that could work if the subcomponents were being written manually
However, it is rather impossible with the @ContributesAndroidInjector annotation, which makes this quite tricky
@kingsleyadio not very hard with @ContributesAndroidInjector. Simplest way is to add a component-local module like so:
@Module
abstract class ApplicationModule {
@ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun provideActivity(): MainActivity
}
@Module
abstract class MainActivityModule {
@Binds
abstract fun provideActivity(mainActivity: MainActivity): AppCompatActivity
}
Then you have AppCompatActivity bound in the scope of the MainActivity subcomponent IIRC
@liminal I agree this will work, and I wouldn't mind if I have to do this once so that it applies to all the activities provided by the module (the ApplicationModule using your example).
However, the issue is having to implement this extra module (which basically does the same thing) for every new activity/fragment that is subscribed to the graph via @ContributesAndroidInjector
How about an annotation param in @ContributesAndroidInjector.
@Module
abstract class ApplicationModule {
@ContributesAndroidInjector(additionalBinds = [Activity::class, Interface::class, ...])
abstract fun provideActivity(): MainActivity
}
class MainActivity : AppCompatActivity, Interface {
}
With this "additionalBinds" parameter in @ContributesAndroidInjector we can specify the addtional binds functions getting created.
The benefit of this "additionalBinds" parameter is, that we are not limited to the supertypes of specified classes.
Hey @Bodo1981. That's a good suggestion.
Perhaps, it could even be used as a meta-annotation, so that we can do something like:
@ContributesAndroidInjector(additionalBinds = [Activity::class, AppCompatActivity::class, ...])
annotation class ContributesActivity
So, we can simply use the @ContributesActivity whenever those additional bindings are required
@Bodo1981 How would that work with Qualifiers?
@GeorgePetri Can you please give me an example where you use Qualifiers with @ContributesAndroidInjector?
@Module
abstract class ApplicationModule {
@ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun provideActivity(): MainActivity
@Binds
@FromApplication
abstract fun provideApplicationContext(app: App): Context
}
@Module
abstract class MainActivityModule {
@Binds
abstract fun provideActivity(mainActivity: MainActivity): AppCompatActivity
@Binds
@FromActivity
abstract fun provideContext(mainActivity: MainActivity): Context
}
@FromActivity and @FromApplication are qualifiers
Maybe we could have a way to define an abstract generic module and use a type parameter for the concrete activity type. Then you can specify the module and Dagger will implement it for you:
Something like this:
@Module
@ContributesAndroidInjector.GenericModule // with a better name
interface ActivityModule<T extends Activity> {
@Binds AppCompatActivity bind(T activity);
@Binds @MySpecialQualifier Context context(T t);
}
You implement it once, include it in all of your @ContributesAndroidInjector declarations, and Dagger handles the rest. cc: @netdpb
Any news about this issue?
Will it ever be implemented? This feature'd save us from writing a boilerplate and would make the code much cleaner
@andretietz, actually builder of component or subcomponent created with @ContributesAndroidInjector provides an Activity (or whatever class you wrote). I suggest you make your @Provides methods in ActivityModule static, or in case of Kotlin, make your @Module class an object and annotate @Provides functions with @JvmStatic like this:
@Module
object ActivityModule {
@Provides
@ActivityScope
@JvmStatic
fun provideObjectDependentFromActivity(someActivity: SomeActivity) = SomeObject(someActivity)
}
So there will be no reason to create subcomponents the long way and you can use @ContributesAndroidInjector without any @Provides or @Binds methods in your components which provide you an Activity instance.
I had the same issue and making @Provides functions static solved it. Maybe it will solve your issue too.
Any news about this
Closing this as with Dagger Hilt's release there's unlikely to be significant new features added to Dagger Android.
Most helpful comment
Maybe we could have a way to define an abstract generic module and use a type parameter for the concrete activity type. Then you can specify the module and Dagger will implement it for you:
Something like this:
You implement it once, include it in all of your @ContributesAndroidInjector declarations, and Dagger handles the rest. cc: @netdpb