Android 102 : Why does dagger generate MembersInjector for us?

Saurabh Kumar
Geek Culture
Published in
3 min readMar 13, 2022

--

We ended the previous article with a simple dependency injection example. now I want to make that a tad more complex by adding MembersInjector into the mix 😈!

We introduced MembersInjector as something which helps us inject all dependencies in a class at once, instead of doing it separately for each dependency. i.e

dummy = DaggerDummyComponent.create().getDummy()  <---- not thisDaggerDummyComponent.create().inject(this)      <---- all at once

Here is when MembersInjector comes into the picture!
Dagger generates a members-injector for classes having@Inject applied to a field or a method. Which will be used for injecting all the dependencies inside any class (for us it's the main activity) at a time.

There is also a definition in the official document for MembersInjector :

Members injection methods are void methods on a component that takes a parameter of a specific type, allowing Dagger to set its @Inject-annotated fields and call its @Inject-annotated methods.

So to sum up, this example dagger generates a MembersInjector in addition to the 2 classes we already discussed in the previous article, i.e the DaggerDummyComponent and the DummyFactory.

This is the component with an inject function entry point.

@Component
@Singleton
interface DummyComponent {
fun inject(activity: MainActivity)
}

and the generated dagger component code.

@DaggerGenerated
public final class DaggerDummyComponent implements DummyComponent {
private final DaggerDummyComponent dummyComponent = this;

private Provider<Dummy> dummyProvider;

private DaggerDummyComponent() {
initialize();
}

public static Builder builder() {
return new Builder();
}

public static DummyComponent create() {
return new Builder().build();
}

@SuppressWarnings("unchecked")
private void initialize() {
dummyProvider = DoubleCheck.provider(Dummy_Factory.create());
}
// We get one such overriden method for each binding we declare
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}

private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectDummy(instance,
dummyProvider.get());
return instance;
}

public static final class Builder {
private Builder() {
}

public DummyComponent build() {
return new DaggerDummyComponent();
}
}
}

Since we have defined a binding in form of the main activity itself the component used a membersInjector class to get the dependencies injected in the main activity

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
// We will get one such factory for each dependency,
// Injected inside the mainActivity

private final Provider<Dummy> dummyProvider;

public MainActivity_MembersInjector(Provider<Dummy> dummyProvider){
this.dummyProvider = dummyProvider;
}

public static MembersInjector<MainActivity> create(Provider<Dummy> dummyProvider) {
return new MainActivity_MembersInjector(dummyProvider);
}
@Override
public void injectMembers(MainActivity instance) {
injectDummy(instance, dummyProvider.get());
// we can have more injected members here
}

@InjectedFieldSignature("com.example.dagger_demo.MainActivity.dummy)
public static void injectDummy(MainActivity instance, Dummy dummy){
instance.dummy = dummy;
}
}

Details to look out for!

  1. When we add @inject annotation on the lateinit var or any method inside any class, Dagger generates a membersInjector for the class itself. Which has all the factories for the dependencies initialized inside this class. In this example, the class was the main activity and the injected dependency was of type Dummy.
  2. Dagger knows that this isn’t a normal component entry point method because it doesn’t return anything and it takes a parameter. When you pass a mainActivity to the inject method in the above example, Dagger will set any @Inject fields and call any @Inject methods on it.
  3. What is this DoubleCheck? It always pops up in the generated component implementation, if we use scoped annotations like @singleton.
private void initialize() {
this.dummyProvider = DoubleCheck.provider(Dummy_Factory.create());
}

I wanted to write about it but came across this article which explains everything I wanted to write about, so being lazy I did not write about it 👀.

Until now in the dagger articles, we have only dealt with the classes which can have @inject annotated constructors i.e they don't need a module to specify how to construct an object.
But what happens if we require an object to be injected but we don't have access to its constructor? maybe some external library class?
that's when modules enter the party 🥳.

We will look at them in another article here

leave me a clap if you found this article insightful 😄, Happy Reading !

--

--