[JAVA] Understand what Dagger2's Android support is doing

Overview

Dagger2 includes dagger.android from 2.10, and 2.11 makes it even easier to use. Here, I will look at the generated code and write an explanation of what I am doing. I will also describe how to install using android support in Kotlin.

Introduction

Gradle

build.gradle


buildscript {
    ext.dagger_version = "2.12"
・ ・ ・

app/build.gradle


apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

・ ・ ・
dependencies {
・ ・ ・
    kapt "com.google.dagger:dagger-compiler:$dagger_version"
    kapt "com.google.dagger:dagger-android-processor:$dagger_version"
    implementation "com.google.dagger:dagger:$dagger_version"
    implementation "com.google.dagger:dagger-android:$dagger_version"
・ ・ ・

Module

The following is how to write the Module added in Android Support.

AndroidModule.kt


@Module
abstract class AndroidModule {

    @ContributesAndroidInjector
    abstract fun contributeMainActivity(): MainActivity
}

@ContributesAndroidInjector is 2.It is an API made from 11.


 This will generate the code you wrote in 2.10.
 The auto-generated source will be explained later.

 Component


#### **`AppComponent.kt`**
```kt

@Singleton
@Component(modules = arrayOf(AndroidInjectionModule::class, AndroidModule::class))
interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: App): Builder
        fun build(): AppComponent
    }
    fun inject(app: App)
}

AndroidInjectionModule::class is miso. 2.Added from 10.



#### **`@BindsInstance is 2.Added from 9. When you build the component yourself, you can inject the argument into the component by calling the method that specifies it.`**
```Added from 9. When you build the component yourself, you can inject the argument into the component by calling the method that specifies it.



 What this means is that if you don't write ``` @BindsInstance fun application (application: App): Builder```, for example, if you try to inject the Context as shown below, you will get a compile error.

@Module class AppModule { @Provides fun provideContext(application: App) = application.applicationContext }

class MainDao @Inject constructor() { @Inject lateinit var context: Context



#### **`@BindsInstance fun application(application: App):By describing the Builder and setting it in the Application class, it can be used in the DI state in the Module.`**

Application

Build the component using the class `` `DaggerAppComponent``` automatically generated from AppComponent.

App.kt


class App : Application(), HasActivityInjector {

    @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun activityInjector() = dispatchingAndroidInjector

    override fun onCreate() {
        super.onCreate()

        val objectGraph = DaggerAppComponent
                .builder()
                .application(this)
                .build()
        objectGraph.inject(this)
    }
}

The App class is made available in Module by using the method of @BindsInstance specified in AppComponent. (This is the part of application (this) `` `.) Also, the `` `dagger.android.HasActivityInjector interface is important. This is explained with the following Activity source.

Activity

Thanks to Android support, you can now just write ``` dagger.android.AndroidInjection.inject (this)` `` and it will be DI.

MainActivity.kt


lass MainActivity : AppCompatActivity() {
    @Inject
    lateinit var viewModel: MainViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidInjection.inject(this)
・ ・ ・

I will also post other classes.

MainViewModel.kt


class MainViewModel @Inject constructor() {
    @Inject
    lateinit var domain: MainDomain 
・ ・ ・

dagger.android.AndroidInjection.inject(this)What is specifically doing is the Application class(App in this case.kt class)With dagger.android.If HasActivityInjector is implemented, dagger.android.Dispatching Using AndroidInjector Argument Activity(Here is MainActivity)Componet(This is a class for resolving dependencies. Also known as ObjectGraph.)I put it in. (@If you don't write ContributesAndroidInjector, you will get an assert error here. )


 Then, it will inject into the field that is `` `@ Inject``` in Activity.

 More specifically, `` `dagger.android.DispatchingAndroidInjector` `` is the code `` `MainActivitySubcomponentBuilder` `` automatically generated by `` `@ ContributesAndroidInjector` `` defined in Module with MainActivity as the key, and more. , Builder also instantiates the automatically generated class `` `MainActivitySubcomponentImpl``` (the implementation class of` `dagger.android.AndroidInjector```), and this class injects each class.

 Below is a portion of the corresponding code described.
 The following maybeInject method is located before AndroidInjection.inject (this) of Activity is called.


#### **`dagger.android.DispatchingAndroidInjector.java`**

public boolean maybeInject(T instance) { // ① instance=MainActivity Provider<AndroidInjector.Factory<? extends T>> factoryProvider = injectorFactories.get(instance.getClass()); if (factoryProvider == null) { return false; }

@SuppressWarnings("unchecked")
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get(); // ② factory=MainActivitySubcomponentBuilder

try {
  AndroidInjector<T> injector =
      checkNotNull(
          factory.create(instance), // ③ new MainActivitySubcomponentImpl()             
          "%s.create(I) should not return null.",
          factory.getClass().getCanonicalName());

  injector.inject(instance); // ④

 The argument `` `T instance``` in line ① is MainActivity.
 MainActivitySubcomponentBuilder is entered in ```AndroidInjector.Factory <T> factory``` in line ②.
 MainActivitySubcomponentImpl is instantiated with ``` factory.create (instance)` `` in line ③.
 In line ④, ```injector.inject (instance);` ``, the `` `MembersInjector <MainActivity>` `` held by the automatically generated `` `DaggerAppComponent``` (also automatically generated) `` `MainActivity_MembersInjector``` is the implementation class) is used to set the class to be DI in MainActivity.


# Automatically generated code

```@contributesandroidinjector```The code generated by is below.
 In version 2.10 you had to write this yourself.



#### **`AndroidModule_ContributeMainActivity.java`**
```java

@Module(subcomponents = AndroidModule_ContributeMainActivity.MainActivitySubcomponent.class)
public abstract class AndroidModule_ContributeMainActivity {
  private AndroidModule_ContributeMainActivity() {}

  @Binds
  @IntoMap
  @ActivityKey(MainActivity.class)
  abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
      MainActivitySubcomponent.Builder builder);

  @Subcomponent
  public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {} // implemenets AndroidInjector.Factory
  }
}

@bindsIs an annotation that saves you from writing the following standard code in a very simple way.

@Provides
AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(){
    return new MainActivitySubcomponent.Builder()
}

Actually, `` `MainActivitySubcomponent.Builder``` is also abstract, so you have to implement the necessary methods, but that's what it is.

@With IntoMap@ActivityKey is used as a set. The one specified here is dagger.android.It will be a Map held by DispatchingAndroidInjector.(This Map will be important in the end)




#### **`@The value specified by ActivityKey (in this case, MainActivity).class) is the key. The value is the return value of the method (in this case MainActivitySubcomponent).Become a builder. By the way, this Builder is an Android Injector.It will be an implementation of Factory.`**

The following is the target part automatically generated from AppComponent.

DaggerAppComponent.java


・ ・ ・
 private Provider<AndroidInjector.Factory<? extends Activity>> bindAndroidInjectorFactoryProvider;
・ ・ ・
     @Override
    public AppComponent build() {
      if (application == null) {
        throw new IllegalStateException(App.class.getCanonicalName() + " must be set");
      }
      return new DaggerAppComponent(this);
    }
・ ・ ・
  private DaggerAppComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.mainActivitySubcomponentBuilderProvider =
        new dagger.internal.Factory<
            AndroidModule_ContributeMainActivity.MainActivitySubcomponent.Builder>() {
          @Override
          public AndroidModule_ContributeMainActivity.MainActivitySubcomponent.Builder get() {
            return new MainActivitySubcomponentBuilder();
          }
        }; //three

    this.bindAndroidInjectorFactoryProvider = (Provider) mainActivitySubcomponentBuilderProvider; //Two

    this.mapOfClassOfAndProviderOfFactoryOfProvider =
        MapProviderFactory
            .<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>builder(1)
            .put(MainActivity.class, bindAndroidInjectorFactoryProvider)
            .build(); //Ichi bindAndroidInjectorFactoryProvider=dagger.internal.Factory()Anonymous class of implements Provider. Above 2,See, see

    this.dispatchingAndroidInjectorProvider =
        DispatchingAndroidInjector_Factory.create(mapOfClassOfAndProviderOfFactoryOfProvider);

    this.appMembersInjector = App_MembersInjector.create(dispatchingAndroidInjectorProvider);
  }
・ ・ ・

Let's take a concrete look at the "map of MainActivity.class is the key and the value is MainActivitySubcomponent.Builder" that I explained as important. The bindAndroidInjectorFactoryProvider specified in the map value of Ichi is Looking at the second, it becomes the mainActivitySubcomponentBuilderProvider. In addition, mainActivitySubcomponentBuilderProvider is an anonymous class of dagger.internal.Factory.

dagger.internal.Factory is javax.inject.The instance that inherits from Provider and is returned by get is the MainActivitySubcomponentBuilder class.



#### **`The parent class of MainActivitySubcomponentBuilder is MainActivitySubcomponent.Become a builder.`**
```Become a builder.


 By the way, in Dagger, if the class that can be obtained by `` `javax.inject.Provider # get ()` `` can be injected, the Provider can be DI target.
 In other words, the following definition is unnecessary.

@Provides fun provideMainActivitySubcomponentBuilderProvider() = object: Provider { override fun get(): MainActivitySubcomponentBuilder { return MainActivitySubcomponentBuilder() } }


 Therefore, the key is MainActivity and the value is `` `Provider <AndroidInjector.Factory <? Extends Activity >>` ``.

 It is a continuation. It is a continuation of DaggerAppComponent.java.


#### **`DaggerAppComponent.java`**
```java

  private final class MainActivitySubcomponentBuilder
      extends AndroidModule_ContributeMainActivity.MainActivitySubcomponent.Builder {
    private MainActivity seedInstance;

    @Override
    public AndroidModule_ContributeMainActivity.MainActivitySubcomponent build() {
      if (seedInstance == null) {
        throw new IllegalStateException(MainActivity.class.getCanonicalName() + " must be set");
      }
      return new MainActivitySubcomponentImpl(this); //"A"
    }

    @Override
    public void seedInstance(MainActivity arg0) {
      this.seedInstance = Preconditions.checkNotNull(arg0);
    }
  }

  private final class MainActivitySubcomponentImpl
      implements AndroidModule_ContributeMainActivity.MainActivitySubcomponent {
    private MembersInjector<MainViewModel> mainViewModelMembersInjector;

    private Provider<MainViewModel> mainViewModelProvider;

    private MembersInjector<MainActivity> mainActivityMembersInjector;

    private MainActivitySubcomponentImpl(MainActivitySubcomponentBuilder builder) {
      assert builder != null;
      initialize(builder);
    }

    @SuppressWarnings("unchecked")
    private void initialize(final MainActivitySubcomponentBuilder builder) { //"I"

      this.mainViewModelMembersInjector =
          MainViewModel_MembersInjector.create(MainDomain_Factory.create()); 

      this.mainViewModelProvider = MainViewModel_Factory.create(mainViewModelMembersInjector);

      this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainViewModelProvider);
    }

    @Override
    public void inject(MainActivity arg0) {
      mainActivityMembersInjector.injectMembers(arg0); //"U"
    }
  }

MainActivitySubcomponent.Builder instantiates MainActivitySubcomponentImpl with "A". The actual instance of this is the part ③ factory.create (instance)`` explained in ``dagger.android.DispatchingAndroidInjector.java, which is` It will be called from AndroidInjection.inject (this) `, which is called at the beginning of` ʻActivity # onCreate () `.

Then, when `MainActivitySubcomponentImpl``` is instantiated, the method of" A "holds the Provider of the class you want to DI with ``` @ Inject``` ~ Injector class is automatically generated in the Factory class. Instance it and set it in the field that describes `@ Inject``` of the passed Activity with "U".

The MainActivity_MembersInjector used in "U" is as follows.

MainActivity_MembersInjector.java


public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final Provider<MainViewModel> viewModelProvider;

  public MainActivity_MembersInjector(Provider<MainViewModel> viewModelProvider) {
    assert viewModelProvider != null;
    this.viewModelProvider = viewModelProvider;
  }

  public static MembersInjector<MainActivity> create(Provider<MainViewModel> viewModelProvider) {
    return new MainActivity_MembersInjector(viewModelProvider);
  }

  @Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.viewModel = viewModelProvider.get();
  }
}

Above, the instance of viewModelProvider is below.

MainViewModel_Factory.java


public final class MainViewModel_Factory implements Factory<MainViewModel> {
  private final MembersInjector<MainViewModel> mainViewModelMembersInjector;

  public MainViewModel_Factory(MembersInjector<MainViewModel> mainViewModelMembersInjector) {
    assert mainViewModelMembersInjector != null;
    this.mainViewModelMembersInjector = mainViewModelMembersInjector;
  }

  @Override
  public MainViewModel get() {
    return MembersInjectors.injectMembers(mainViewModelMembersInjector, new MainViewModel());
  }

  public static Factory<MainViewModel> create(
      MembersInjector<MainViewModel> mainViewModelMembersInjector) {
    return new MainViewModel_Factory(mainViewModelMembersInjector);
  }
}

@Singleton

The following will result in `` `@ Singleton```.

@Module
class AppModule {
    @Singleton
    @Provides
    fun provideMainDomain() = MainDomain()
}
@Singleton
class MainDomain @Inject constructor()
@Singleton
@Component(modules = arrayOf(AndroidInjectionModule::class, AndroidModule::class, AppModule::class))
interface AppComponent 
・ ・ ・

The code on the user side is as usual.

class MainViewModel @Inject constructor() {

    @Inject
    lateinit var domain: MainDomain
・ ・ ・

Let's see what happens when we do this

First, if you don't attach it.

DaggerAppComponent.java


・ ・ ・
  private final class MainActivitySubcomponentImpl
      implements AndroidModule_ContributeMainActivity.MainActivitySubcomponent {
・ ・ ・
    private void initialize(final MainActivitySubcomponentBuilder builder) { 

      this.mainViewModelMembersInjector =
          MainViewModel_MembersInjector.create(MainDomain_Factory.create());  //Here is different
・ ・ ・

The Provider class of MainDomain is as follows. This class also exists with @ Singleton.

MainDomain_Factory.java


public final class MainDomain_Factory implements Factory<MainDomain> {
  private static final MainDomain_Factory INSTANCE = new MainDomain_Factory();

  @Override
  public MainDomain get() {
    return new MainDomain();
  }

  public static Factory<MainDomain> create() {
    return INSTANCE;
  }
}

mainviewmodel_membersinjector#createThe argument passed to is maindomain_In the factory, it gets()The method instantiates and returns the maindomain. factory=In the provider, it is instantiated every time get is called.

The following is the case with @ Singleton. mainviewmodel_membersinjector#createThe provider to pass to is different.

DaggerAppComponent.java


      this.mainViewModelMembersInjector =
          MainViewModel_MembersInjector.create(DaggerAppComponent.this.provideMainDomainProvider); //Here is different
  public static AppComponent.Builder builder() {
    return new Builder();
  }
  
  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
・ ・ ・
    this.provideMainDomainProvider =
     DoubleCheck.provider(AppModule_ProvideMainDomainFactory.create(builder.appModule));

This `DoubleCheck.provider ()` method holds the MainDomain for Singleton in the instance field and returns it when the MainDomainProvider is called.

DoubleCheck.java


public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
  private static final Object UNINITIALIZED = new Object();

  private volatile Provider<T> provider;
  private volatile Object instance = UNINITIALIZED;

  private DoubleCheck(Provider<T> provider) {
    assert provider != null;
    this.provider = provider;
  }

  @Override
  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get(); // provider=AppModule_ProvideMainDomainFactory. MainDomain is instantiated here
          Object currentInstance = instance;
          if (currentInstance != UNINITIALIZED && currentInstance != result) {
            throw new IllegalStateException("Scoped provider was invoked recursively returning "
                + "different results: " + currentInstance + " & " + result + ". This is likely "
                + "due to a circular dependency.");
          }
          instance = result; //Since MainDomain is cached here, the same instance is returned every time it is called.
          provider = null;
        }
      }
    }
    return (T) result;
  }

  public static <T> Provider<T> provider(Provider<T> delegate) {
    checkNotNull(delegate);
    if (delegate instanceof DoubleCheck) {
      return delegate;
    }
    return new DoubleCheck<T>(delegate); // App.Called by kt
  }

By the way, the cached DoubleCheck instance is called from App.kt. For activities etc., you will get an instance from the cache using provideMainDomainProvider inside ```AndroidInjection.inject (this)` ``.

Recommended Posts

Understand what Dagger2's Android support is doing
[Android] What is Context? ??
What is Pullback doing in The Composable Architecture
What is Cubby
What is Docker?
What is null? ]
What is java
What is Keycloak
What is maven?
What is Jackson?
What is Docker
What is self
What is Jenkins
What is ArgumentMatcher?
What is IM-Juggling?
What is params
What is SLF4J?
What is Facade? ??
What is Java <>?
What is Gradle?
What is POJO
What is Java
After all, what is [rails db: migrate] doing?
What is centOS
What is RubyGem?
What is programming?
What is before_action?
What is Docker
What is Byte?
What is Tomcat
What is Maven Assembly?
What is `docker-compose up`?
What is vue cli
What is an interface?
What is Ruby's self?
What is hard coding?
What is a stream
What is Ruby's attr_accessor?
What is Java Encapsulation?
What is permission denied?
What is instance control?
What is an initializer?
What is Spring Tools 4
What is an operator?
What is object orientation?
What is Guava's @VisibleForTesting?
What is MVC model?
What is an annotation?
What is Java technology?
What is Java API-java
What is @ (instance variable)?
What is Gradle's Artifact?
What is JPA Auditing?
[Swift] What is dismiss?
[Java] What is flatMap?
What is a Servlet?
What is web development?
[Java] What is JavaBeans?
[Java] What is ArrayList?
[Ruby] What is true?