Android : Mise en place de Dagger 2

Aide-mémoire à partir du CodeLaB : https://developer.android.com/codelabs/android-dagger#0

Ajouter Dagger 2 au projet

Modifier le fichier app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
...
dependencies {    
    ...    
    def dagger_version = "2.27"    
    implementation "com.google.dagger:dagger:$dagger_version"    
    kapt "com.google.dagger:dagger-compiler:$dagger_version"
}

Créer le graphe de dépendances (Dagger graph)

Dans un package « di », créer une interface (composant Dagger) qui sera annotée avec @Component

// Définition du composant Dagger qui tient compte des modules faisant partie du graphe de dépendances
// Le composant est aussi annoté par @Singleton qui définit le temps de vie du composant, temps de vie sur lequel va se caler le temps de vie des objets injectés et dont la classe est annotée par @Singleton
@Singleton
@Component(modules = [MyModule::class])
interface AppComponent { 

    // Factory pour créer les instances de AppComponent
    @Component.Factory
    interface Factory {
        // Rendre le Context disponible dans le graphe de dépendances
        // N.B : @BindsInstance est utilisé pour des objets construits en dehors du graphe de dépendances
        fun create(@BindsInstance context: Context): AppComponent
    } 
  
    // Classes pouvant bénéficier de l'injection de dépendance gérée par ce composant
    fun inject(activity: MyActivity)
    fun inject(activity: OtherActivity)
    fun inject(fragment: MyFragment)
    fun inject(fragment: OtherFragment)
}

Attacher le graphe au cycle de vie de l’application

Créer une classe qui hérite de Application() pour y déclarer une instance de AppComponent (ne pas oublier de déclarer cette classe dans le fichier AndroidManifest.xml)

open class MyApplication : Application() { 
   
    // Instance de AppComponent utilisé dans toute l'application    
    val appComponent: AppComponent by lazy {        
        // Création de l'instance de AppComponent en utilisant son constructeur de Factory      
        // On passe en paramètre applicationContext qui va être utilisé comme Context dans le graphe de dépendances
        DaggerAppComponent.factory().create(applicationContext)    
    }    

}

Initialiser l’injection dans les classes concernées

Cas d’injection dans une Activity

class MyActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {

        // Demander à Dagger d'injecter les dépendances dans cette classe
        // Bonne pratique : le faire avant super.onCreate
        (application as MyApplication).appComponent.inject(this)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)
        ...
    }
    ...
}

Cas d’injection dans un Fragment

class MyFragment : Fragment() {
    override fun onAttach(context: Context) {
        super.onAttach(context)
        
        // Demander à Dagger d'injecter les dépendances dans cette classe
        // Bonne pratique : le faire avant super.onAttach
        (requireActivity().application as MyApplication).appComponent.inject(this)
    }
    ...
}

Notes

Interagir avec le graphe de dépendances

Il y a 2 différentes manière d’interagir avec le graphe de dépendences construit par Dagger :
1 – Déclarer une fonction qui retourne un type Unit et qui prenne une classe en paramètre. Ceci autorise alors l’injection par champs dans cette classe.

exemple : fun inject(activity: MyActivity)

2 – Déclarer une fonction qui retourne un type. Ceci permet de retrouver des éléments de ce type dans dans le graphe.

exemple : fun myComponent(): MyComponent.Factory

Règles concernant les Scope

1 – Quand un type est marqué d’une annotation de scope particulier, il ne peut être utilisé que par des Components marqués de la même annotation de scope.

2 – Quand un Component est marqué d’une annotation de scope particulier, il ne peut fournir que des objets dont la classe est annoté de la même annotation de scope ou des objets dont la classe est sans annotation de scope.

3 – Un SubComponent ne peut pas utiliser la même annotation de scope que l’un de ses Components parents.