[JAVA] Comprendre ce que fait le support Android de Dagger2

Aperçu

Dagger2 inclut dagger.android à partir de 2.10 et 2.11 le rend encore plus facile à utiliser. Voici un aperçu du code généré et une description de ce qu'il fait. En passant, je décrirai également comment installer en utilisant le support Android dans 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

Vous trouverez ci-dessous comment écrire le module ajouté dans le support Android.

AndroidModule.kt


@Module
abstract class AndroidModule {

    @ContributesAndroidInjector
    abstract fun contributeMainActivity(): MainActivity
}

@ContributesAndroidInjector est 2.C'est une API composée de 11.


 Cela générera le code que vous avez écrit en 2.10.
 La source générée automatiquement sera expliquée plus tard.

 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::la classe est le miso. 2.Ajouté à partir de 10.



#### **`@BindsInstance vaut 2.Ajouté à partir de 9. Lorsque vous créez votre propre composant, vous pouvez injecter des arguments dans le composant en appelant la méthode qui le spécifie.`**
```Ajouté à partir de 9. Lorsque vous créez votre propre composant, vous pouvez injecter des arguments dans le composant en appelant la méthode qui le spécifie.



 Cela signifie que si vous n'écrivez pas `` `` @BindsInstance fun application (application: App): Builder ''), par exemple, si vous essayez d'injecter le contexte comme indiqué ci-dessous, vous obtiendrez une erreur de compilation.

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

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



#### **`@BindsInstance fun application(application: App):En décrivant le générateur et en le définissant dans la classe Application, il peut être utilisé dans l'état DI dans le module.`**

Application

Construisez le composant en utilisant la classe `` DaggerAppComponent '' générée automatiquement à partir d'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)
    }
}

La classe App est mise à disposition dans Module à l'aide de la méthode de @ BindsInstancespécifiée dans AppComponent. (C'est la partie deapplication (this) `.) En outre, l'interface dagger.android.HasActivityInjector '' est importante. Ceci est expliqué avec la source d'activité suivante.

Activity

Grâce au support Android, vous pouvez maintenant simplement écrire `` dagger.android.AndroidInjection.inject (this) '' et ce sera DI.

MainActivity.kt


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

Je publierai également d'autres classes.

MainViewModel.kt


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

dagger.android.AndroidInjection.inject(this)Ce que fait spécifiquement la classe Application(App dans ce cas.classe kt)Avec poignard.android.Si HasActivityInjector est implémenté, poignard.android.Activité d'argument à l'aide de DispatchingAndroidInjector(Voici MainActivity)Composant(C'est une classe pour résoudre les dépendances. Aussi connu sous le nom d'ObjectGraph.)Je l'ai mis. (@Si vous n'écrivez pas ContributesAndroidInjector, vous obtiendrez une erreur d'assertion ici. )


 Ensuite, il injectera dans le champ `` `` @ Inject``` dans Activity.

 Plus spécifiquement, `` `` dagger.android.DispatchingAndroidInjector` '' `` est le code `` MainActivitySubcomponentBuilder` `` généré automatiquement par `` `` @ ContributesAndroidInjector` `` défini dans Module avec MainActivity comme clé, et plus encore. , Builder génère également la classe générée automatiquement `` MainActivitySubcomponentImpl '' (la classe d'implémentation de `` dagger.android.AndroidInjector ''), et cette classe injecte chaque classe.

 Voici une partie du code correspondant décrit.
 La méthode mayInject suivante est située avant l'appel d'AndroidInjection.inject (this) d'Activity.


#### **`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); // ④

 L'argument `` T instance '' à la ligne ① est MainActivity.
 MainActivitySubcomponentBuilder est entré dans ```AndroidInjector.Factory <T> factory``` sur la ligne ②.
 MainActivitySubcomponentImpl est instancié avec `` `` factory.create (instance) '' dans la ligne ③.
 Dans la ligne ④, ```injector.inject (instance); `` `,` `` `MembersInjector <MainActivity>` `` `(également généré automatiquement) détenu par le` `DaggerAppComponent`` généré automatiquement `` MainActivity_MembersInjector '' est la classe d'implémentation) est utilisé pour définir la classe comme DI dans MainActivity.


# Code généré automatiquement

```@contributesandroidinjector```Le code généré par est ci-dessous.
 Dans la version 2.10, vous deviez l'écrire vous-même.



#### **`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
  }
}

@bindsEst une annotation qui vous évite d'écrire le code standard suivant d'une manière très simple.

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

En fait, `` MainActivitySubcomponent.Builder '' est également abstrait, vous devez donc implémenter les méthodes nécessaires, mais c'est ce que c'est.

@Avec IntoMap@ActivityKey est utilisé comme un ensemble. Celui spécifié ici est le poignard.android.Ce sera une carte détenue par DispatchingAndroidInjector.(Cette carte sera importante à la fin)




#### **`@La valeur spécifiée par ActivityKey (dans ce cas, MainActivity).class) est la clé. La valeur est la valeur de retour de la méthode (dans ce cas, MainActivitySubcomponent).Devenez un constructeur. Au fait, ce constructeur est un injecteur Android.Ce sera une implémentation de Factory.`**

Voici la partie cible générée automatiquement à partir d'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();
          }
        }; //Trois

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

    this.mapOfClassOfAndProviderOfFactoryOfProvider =
        MapProviderFactory
            .<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>builder(1)
            .put(MainActivity.class, bindAndroidInjectorFactoryProvider)
            .build(); //Ichi bindAndroidInjectorFactoryProvider=dagger.internal.Factory()Classe anonyme d'outils Provider. Au-dessus de 2,Voir voir

    this.dispatchingAndroidInjectorProvider =
        DispatchingAndroidInjector_Factory.create(mapOfClassOfAndProviderOfFactoryOfProvider);

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

Jetons un regard concret sur la "carte de MainActivity.class est la clé et la valeur est MainActivitySubcomponent.Builder" que j'ai expliqué comme importante. Le bindAndroidInjectorFactoryProvider spécifié dans la valeur de mappage d'Ichi est En regardant le second, il devient le mainActivitySubcomponentBuilderProvider. De plus, mainActivitySubcomponentBuilderProvider est une classe anonyme de `` dagger.internal.Factory ''.

dagger.internal.L'usine est javax.inject.L'instance qui hérite de Provider et est retournée par get est la classe MainActivitySubcomponentBuilder.



#### **`La classe parente de MainActivitySubcomponentBuilder est MainActivitySubcomponent.Devenez un constructeur.`**
```Devenez un constructeur.


 D'ailleurs, dans Dagger, si la classe qui peut être obtenue par `` javax.inject.Provider # get () '' peut être injectée, le Provider peut être une cible DI.
 En d'autres termes, la définition suivante n'est pas nécessaire.

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


 Par conséquent, la clé est MainActivity et la valeur est `` Provider <AndroidInjector.Factory <? Extends Activity >> ''.

 C'est une continuation. C'est une continuation de 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); //"UNE"
    }

    @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) { //"JE"

      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 instancie MainActivitySubcomponentImpl avec "A". L'instance réelle de ceci est la partie ③ factory.create (instance) '' expliquée dans dagger.android.DispatchingAndroidInjector.java '', qui est, après tout, Il sera appelé depuis ```AndroidInjection.inject (this) `, qui est appelé au début de` ʻActivity # onCreate ()`.

Ensuite, lorsque MainActivitySubcomponentImpl est instancié, la méthode de "A" contient le fournisseur de la classe que vous voulez DI avec `` `` @ Inject ~ La classe d'injecteur est automatiquement générée dans la classe Factory. Instancez-le et définissez-le dans le champ qui décrit @ Inject``` de l'activité passée avec "U".

Le MainActivity_MembersInjector utilisé dans "U" est le suivant.

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();
  }
}

Ci-dessus, l'instance de viewModelProvider est ci-dessous.

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

Le résultat suivant donnera `` @ 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 
・ ・ ・

Le code côté utilisateur est comme d'habitude.

class MainViewModel @Inject constructor() {

    @Inject
    lateinit var domain: MainDomain
・ ・ ・

Voyons ce qui se passe quand on fait ça

Premièrement, si vous ne le joignez pas.

DaggerAppComponent.java


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

      this.mainViewModelMembersInjector =
          MainViewModel_MembersInjector.create(MainDomain_Factory.create());  //Voici différent
・ ・ ・

La classe Provider de MainDomain est la suivante. Cette classe existe également avec `` @ 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#createL'argument passé à est maindomain_Dans l'usine, il obtient()La méthode instancie et retourne le maindomain. usine=Dans le fournisseur, il est instancié chaque fois que get est appelé.

Ce qui suit est le cas avec @ Singleton. mainviewmodel_membersinjector#create```Le fournisseur à qui passer est différent.

DaggerAppComponent.java


      this.mainViewModelMembersInjector =
          MainViewModel_MembersInjector.create(DaggerAppComponent.this.provideMainDomainProvider); //Voici différent
  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));

Cette méthode `DoubleCheck.provider ()` contient le MainDomain for Singleton dans le champ ʻinstance``` et le renvoie lorsque le MainDomainProvider est appelé.

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_FournirMainDomainFactory. MainDomain est instancié ici
          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; //Puisque MainDomain est mis en cache ici, la même instance est renvoyée à chaque fois qu'elle est appelée.
          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.Appelé par kt
  }

À propos, l'instance DoubleCheck mise en cache est appelée depuis App.kt. Pour les activités, etc., vous obtiendrez une instance du cache en utilisant provideMainDomainProvider dans ```AndroidInjection.inject (this) `` `.

Recommended Posts

Comprendre ce que fait le support Android de Dagger2
Qu'est-ce que Cubby
Qu'est-ce qui est nul? ]
Qu'est-ce que 'java
Qu'est-ce que Keycloak
Qu'est-ce que maven?
Qu'est-ce que Jackson?
Qu'est-ce que soi
Qu'est-ce que Jenkins
Qu'est-ce que ArgumentMatcher?
Qu'est-ce que IM-Juggling?
Qu'est-ce que les paramètres
Qu'est-ce que SLF4J?
Qu'est-ce que la façade? ??
Qu'est-ce que Java <>?
Qu'est-ce que Gradle?
Qu'est-ce que POJO
Qu'est-ce que 'java
Après tout, que fait [rails db: migrate]?
Qu'est-ce que centOS
Qu'est-ce que RubyGem?
Qu'est-ce que before_action?
Qu'est-ce que Docker
Qu'est-ce que Byte?
Qu'est-ce que Tomcat
Qu'est-ce que l'assemblage Maven?
Qu'est-ce que `docker-compose up`?
Qu'est-ce que vue cli
Qu'est-ce qu'une interface
Qu'est-ce que le moi de Ruby?
Qu'est-ce que le codage en dur?
Qu'est-ce qu'un flux
Qu'est-ce que l'attr_accessor de Ruby?
Qu'est-ce que l'encapsulation Java?
Qu'est-ce qu'une permission refusée?
Qu'est-ce que le contrôle d'instance?
Qu'est-ce qu'un initialiseur?
Qu'est-ce que Spring Tools 4
Qu'est-ce qu'un opérateur?
Qu'est-ce que l'orientation objet?
Qu'est-ce que le @VisibleForTesting de Guava?
Qu'est-ce qu'un modèle MVC?
Qu'est-ce qu'une annotation?
Qu'est-ce que la technologie Java?
Qu'est-ce que Java API-java
Qu'est-ce que @ (variable d'instance)?
Qu'est-ce que l'artefact de Gradle?
Qu'est-ce que l'audit JPA?
[Java] Qu'est-ce que flatMap?
Qu'est-ce qu'un servlet?
Qu'est-ce que le développement Web?
[Java] Qu'est-ce que ArrayList?