E. Fernandes
E. Fernandes

Reputation: 3997

How to implement Dependency Injection using Dagger2 on the given scenario? (Manage injection hierarchy on Dagger2)

Suppose I have the following classes:

class Foo {}

class Bar {
    Foo mFoo = new Foo();
}

class MyActivity extends AppCompatActivity {
     Bar mBar = new Bar();    

    @Override
    protected void onCreate (Bundle savedInstance) {
        super(savedInstance);
    }    
}

From the code above it can be seen that MyActivity depends on Bar and Bar depends on Foo. What is the correct way to implement dependency injection using Dagger2?


My Approach...

So far, the way I've been dealing with this type os scenario was:

Configuration

@Singleton
@Component(
        modules = {
                MainModule.class,
        })
public interface MainComponent {
    void inject(MyActivity myActivity);

    void inject(Bar bar);
}

@Module
class MainModule {
    @Provides
    @Singleton
    Foo provideFoo() {
        return new Foo();
    }

    @Provides
    @Singleton
    Bar provideBar() {
        return new Bar();
    }
}

Injection

class Bar {
    @Inject Foo mFoo;

    Bar() {inject(this);}        
}

class MyActivity extends AppCompatActivity {
    @Inject Bar mBar;

    @Override
    protected void onCreate (Bundle savedInstance) {
        super(savedInstance);
        inject(this);
    }    
}

Problem is, according to this answer, it is not supposed to work like that, Bar should not call inject and have Foo passed as parameter. But, what if I need singletons instances of Foo in other parts of my project? How should I use Dagger2 to achieve the given scenario?

Upvotes: 1

Views: 114

Answers (2)

codeprogression
codeprogression

Reputation: 3441

You do not need to use a module to provide plain classes. You can annotate the classes directly. To always provide Foo as a singleton, you can annotate the class itself.

@Singleton
@Component
public interface MainComponent {
  void inject(MyActivity myActivity);
}

@Singleton
public class Foo {

  @Inject
  public Foo(){}
}

@Singleton
public class Bar {

  private final Foo foo;

  @Inject
  public Bar(final Foo foo){
    this.foo = foo;
  }
}

Modules are required when you need to build/configure instances or provide an implementation of an interface. For example:

@Module
class MainModule {
  @Singleton
  @Provides
  Gson gson() {
    return new GsonBuilder.create();
  }

  @Singleton
  @Provides
  IBar bar(Bar bar){
    return bar;
  }
}

// You can also provide an interface with the @Binds attribute instead
// of using the traditional @Provides annotation
@Module
abstract class MainBindsModule {
  @Singleton
  @Binds
  abstract IBar bar(Bar bar);
}

Upvotes: 1

David Rawson
David Rawson

Reputation: 21437

If you change the signature of the constructor of Bar to the following you should achieve the effect that you want (constructor injection).

class Bar {
    Foo foo;

    @Inject
    Bar(Foo foo) {
        this.foo = foo;
    }        
}

Then you just need to specify the dependency of Bar in the provider by writing it as a method parameter like this:

@Provides Bar provideBar(Foo foo)

Since Foo and Bar are known to dagger through the @Provide methods, dagger will automagically create an instance of Foo to obtain an instance of Bar. Constructor injection like this will have the added advantage of making your code easy to unit test. In your test, you can swap out a real Foo with a mock Foo and test the interaction of Foo on Bar.

Property injection is best only when you don't have access to the constructor (like in Android Activity and Service). For your other dependencies, it's probably best to use constructor injection.

Upvotes: 1

Related Questions