Reputation: 15010
All the following are used with dagger 2.10 and dagger.android
package.
Say I have a class I'd like to inject in multiple activities.
public class DemoSharedClass {
Activity activity;
@Inject public DemoSharedClass(Activity activity) {
this.activity = activity;
}
}
Then, using the latest Dagger APIs, my classes are defined as
public class DemoActivity extends DaggerActivity {
@Inject DemoSharedClass demoSharedClass;
// ...
}
public class Demo2Activity extends DaggerActivity {
@Inject DemoSharedClass demoSharedClass;
// ...
}
Each activity has its module and subcomponent define as (exactement the same for Demo2Activity
)
@Module(subcomponents = DemoActivitySubcomponent.class)
public abstract class DemoActivityModule {
@Binds
@IntoMap
@ActivityKey(DemoActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindDemoActivityInjectorFactory(DemoActivitySubcomponent.Builder builder);
// This is set so SharedClass can be injected using its constructor
// There is the same in Demo2ActivityModule
@Binds abstract Activity bindActivity(DemoActivity demoActivity);
}
@Subcomponent
public interface DemoActivitySubcomponent extends AndroidInjector<DemoActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<DemoActivity> {}
}
Finally, the app component is defined as
@Component(modules = {
ApplicationModule.class,
AndroidInjectionModule.class,
DemoActivityModule.class,
Demo2ActivityModule.class,
})
interface DemoApplicationComponent {
DemoApplication injectApplication(DemoApplication application);
}
Now, I get this error when I build the project:
Error:(11, 11) error: [dagger.android.AndroidInjector.inject(T)] android.app.Activity is bound multiple times:
@Binds android.app.Activity dagger.demo.DemoActivityModule.bindActivity(dagger.demo.DemoActivity)
@Binds android.app.Activity dagger.demo.Demo2ActivityModule.bindActivity(dagger.demo.Demo2Activity)
I get the error, and I think this is because they are in the same scope, the app component.
I thought about creating a common module to both DemoSubcomponents which would look something like the following but with similar error.
@Module
public abstract class SharedClassModule {
@Binds abstract Activity bindContext(DemoActivity demoActivity);
@Binds abstract Activity bindContext(Demo2Activity demo2Activity);
}
Question: how am I supposed to do it?
Upvotes: 3
Views: 1839
Reputation: 95704
You'll need a separate binding on each Subcomponent:
@Module interface DemoActivitySubcomponentModule {
@Binds abstract Activity bindContext(DemoActivity demoActivity);
// ...other bindings unique to DemoActivity and not DemoActivity2
}
@Subcomponent(modules={DemoActivitySubcomponentModule.class})
public interface DemoActivitySubcomponent extends
AndroidInjector<DemoActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<DemoActivity> {}
}
Because of @BindsInstance Builder seedInstance(DemoActivity)
in AndroidInjector.Builder, which Dagger calls in AndroidInjector.Builder.create()
from DispatchingAndroidInjector, Dagger does know how to provide a DemoActivity instance at least. However, there's no built-in binding between DemoActivity and Activity (or Context), so the binding has to be made on the subcomponent instead of the component. By putting the module with that binding on the appropriate @Subcomponent
, you can ensure that within each respective subcomponent the Activity
binding goes to the correct type that Dagger knows about.
Note that the @Binds @IntoMap @ActivityKey(...)
binding in DemoActivityModule still needs to go onto the ApplicationComponent, so the ApplicationComponent can determine which subcomponent to create from the class of the Activity being injected. You specifically want the new DemoActivitySubcomponentModule to go onto DemoActivitySubcomponent so that it makes the Activity-to-DemoActivity in a place where DemoActivity2Subcomponent can't see it.
As a side note, the problem you're seeing is the conflict between the two bindings to Activity
, which happen in the same component. It's not quite right to say the same scope, because (though you might choose to add a scope annotation like @ActivityScope to each subcomponent) there is no scope annotation that would help you here. The seedInstance
will apply only within each currently-unscoped subcomponent, which will combine with bindings in the ancestor components as well as the subcomponent-specific modules.
Upvotes: 5