Dagger 2 -- base classing injections with supplemental modules

1,240 views
Skip to first unread message

Alex Fu

unread,
May 5, 2015, 3:26:36 PM5/5/15
to dagger-...@googlegroups.com
In Dagger 1 I had a base class setup such that it would handle creating a scoped graph and injecting dependencies into the current object. For example...

public abstract class MyBaseActivity extends Activity {
 
private ObjectGraph graph;

 
protected void onCreate(Bundle savedInstanceState) {
    graph
= ((MyApp) getApplication()).plus(getModules());
    graph
.inject(this);
 
}

 
protected Object[] getModules();
}

public class MyClass extends MyBaseActivity {

 
@Inject SomeDep someDep;

 
@Override
 
protected Object[] getModules() {
   
return new Object[/* Contains a module that provides SomeDep */];
 
}
}

This allowed for each subclass to supplement their own set of modules in addition to a standard application module.

After playing around with Dagger 2, it doesn't seem possible to handle a similar scenario...

public abstract class MyBaseActivity extends Activity {
 
private MyBaseActivityComponent component;

 
protected void onCreate(Bundle savedInstanceState) {
    component
= ((MyApp) getApplication()).component().plus(/* Can not accept an array */);
    component
.inject(this);
 
}
}

I worked around the above by modifying MyBaseActivityComponent such that it would list all possible modules it may use...

@Subcomponent(modules = {
 
Module1.class,
  Module2.class
})
public interface MyBaseActivityComponent {
 
public void inject(MyBaseActivity activity);
}

So now I can do something like this...

public abstract class MyBaseActivity extends Activity {
 
private MyBaseActivityComponent component;

 
protected void onCreate(Bundle savedInstanceState) {
    component
= ((MyApp) getApplication()).component().plus(new Module1(), new Module2());
    component
.inject(this);
 
}
}

But now I have a problem where the injection will inject dependencies for MyBaseActivity but not it's subclasses. Suggestions?

Thomas Broyer

unread,
May 15, 2015, 4:00:40 AM5/15/15
to dagger-...@googlegroups.com
I had created Bullet• as a proof-of-concept for this kind of use-cases (you'd need to have one method per subclass in your MyBaseActivityComponent though, see below), but as you don't seem to be afraid of refactoring, I think you should rather change your code to instantiate the component/subcomponent and inject members within the subclasses (whether you keep one "big" component for all your activities or split it into smaller ones; and whether you use subcomponents or components with dependencies)

public abstract class MyBaseActivity extends Activity {
  protected void onCreate(Bundle savedInstanceState) {
    MyAppComponent parentComponent = ((MyApp) getApplication()).component();
    injectMembers(parentComponent);
  }

  protected abstract void injectMembers(MyAppComponent parentComponent);
}

public class MyClass extends MyBaseActivity {

  @Inject SomeDep someDep;

  @Override protected void injectMembers(MyAppComponent parentComponent) {
    // with a subcomponent:
    parentComponent.plus(new Module1()).inject(this);
    // or with a component with dependencies:
    // DaggerMyClassComponent.builder()
    //     .myAppComponent(parentComponent)
    //     .build()
    //     .inject(this);
  }
}

In any case, you need members-injection methods on components for each of your subclass (instead of listing those classes in the injects of the @Module annotation in Dagger 1), and you need to call the appropriate one (see the "note about covariance" in @Component javadoc). This is where Bullet• can help (dynamically selecting the appropriate method to call), but Bullet• is only a crutch and you should refactor your code to avoid it instead (as I show above).
Reply all
Reply to author
Forward
0 new messages