Après avoir testé la base de données pour garantir sa fiabilité et son efficacité, le prochain pas naturel pour vous est de structurer votre application en utilisant l’ensemble des bonnes pratiques et des recommandations de Google. Elles concernent l’injection des dépendances pour gérer les liens entre les classes de votre projet, l'utilisation du patron de conception Repository (dépôt en français) pour gérer vos données mais aussi l’utilisation du patron de conception MVVM pour structurer votre application.
Découvrez l’injection de dépendances avec Hilt
Dans une application mobile, les classes ont généralement besoin d'objets d'autres classes pour fonctionner. Lorsqu'une classe nécessite une autre classe, cette dernière est appelée dépendance. Même s’il peut sembler facile d’instancier vous-même les objets requis, cette approche rend le code inflexible et difficile à tester, car la classe et les objets requis sont fortement liés.
Pour rendre le code plus maintenable et évolutif, une solution consiste à ne pas instancier les objets dont dépend une classe dans celle-ci, mais de les instancier en dehors de celle-ci puis de les transmettre. La transmission des objets est alors appelée injection de dépendances ou dependency injection (DI) en anglais.
Dans le cadre du développement Android, il existe plusieurs bibliothèques populaires permettant de mettre en place l’injection de dépendance dont Hilt une bibliothèque conçue par Google. Elle offre une approche simplifiée et efficace pour injecter des dépendances dans les composants Android, tels que les activités, les fragments et les services.
Ajoutez les dépendances à Hilt
Dans le cadre de l’application PETiSoin, c’est donc Hilt qui sera utilisée pour mettre en place l’injection de dépendances dans le projet.
Comme à chaque nouvelle dépendance, la première étape consiste à ajouter celle-ci au sein du catalogue Gradle.
1. Pour ce faire, rendez-vous dans le dossier Gradle du projet.
2. Ouvrez dans Android Studio le fichier "libs.versions.toml".
[versions]
hilt = "2.51.1"
[libraries]
hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }
[plugins]
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
3. Déclarez l’usage du plugin dans le projet. Pour ce faire, rendez-vous dans le fichier "build.gradle.kts" qui se trouve à la racine du projet Android Studio pour y ajouter, dans la section plugins, le plugin Hilt :
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
//…
alias(libs.plugins.hilt) apply false
}
4. Activez maintenant l’utilisation du plugin au sein du module app du projet Android. Rendez-vous donc dans le fichier "build.gradle.kts" qui se trouve, cette fois-ci, dans le dossier app de votre projet Android. Cherchez la sectionpluginset ajoutez-y une référence au plugin Hilt.
plugins {
//…
alias(libs.plugins.hilt)
}
5. Toujours au sein du fichier "build.gradle.kts" du module app, rendez-vous dans la sectiondependenciespour y ajouter les dépendances aux deux bibliothèques Hilt.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Créez la classe d’application Hilt
Maintenant que Hilt est intégré dans le projet PETiSoin, il est temps d’implémenter les premiers éléments. Toutes les applications qui utilisent Hilt doivent contenir une classe Application
annotée avec@HiltAndroidApp
. Sinon, c’est le crash assuré !
Cette annotation déclenche la génération du code de Hilt, y compris une classe de base de votre application qui sert de conteneur de dépendances au niveau de l'application.
1. Vous devez donc créer une classe PETiSoinApplication
qui hérite de la classe Application
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
2. N’oubliez pas de mettre à jour le fichier "AndroidManifest.xml" du projet pour référencer cette nouvelle classe au sein de la balise<application>
:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
>
<application
android:name=".PETiSoinApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.HexagonalGames"
tools:targetApi="31"
>
</application>
</manifest>
Créez le module Hilt
Si on anticipe l’architecture cible de l’application PETiSoin, nous allons implémenter le patron de conception appelé “Repository” pour exposer les données depuis la couche de données de notre architecture MVVM. Ce patron permet de gérer les sources de données de manière organisée. Et pour interagir avec la base de données, le repository a besoin d’un DAO qu’il convient d’injecter dans son constructeur. Le problème est qu’un DAO est une interface. Il n’est donc pas possible d’injecter un constructeur. C’est pour répondre à cette problématique que nous devons créer un module Hilt pour fournir les informations de liaison et indiquer à Hilt comment fournir des instances de certains types.
Un module Hilt est une classe annotée avec@Module
. Cette classe doit également porter une seconde annotation@InstallIn
afin d’indiquer à Hilt la classe Android dans laquelle chaque module sera utilisé ou installé. Dans le cas de l’application PETiSoin, nous allons installer le module dans la classeApplication
en utilisant le composant SingletonComponent
de Hilt.
Vous pouvez donc créer une classeAppModule
dans le package di du projet PETiSoin et lui faire porter les différentes annotations. Vous aurez l’occasion de la compléter dans la suite de ce chapitre.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Voici une vidéo qui récapitule les principales étapes pour mettre en place Hilt dans votre projet.
Appréhendez le patron de conception MVVM
Si l’on respecte un autre principe architectural poussé actuellement par Google pour le développement d’une application Android moderne, chaque application doit comporter au moins deux couches :
la couche de données (Model) qui contient la logique métier de l’application et expose les données de l'application (repositories et Room);
la couche d'interface utilisateur qui affiche les données de l'application à l'écran (View et Viewmodel).
Implémentez le patron de conception Repository
Maintenant que les bases de l’injection de dépendance sont en place, il est temps de s’attaquer à la couche de données de l’application PETiSoin.
La couche de données est constituée de dépôts qui contiennent une ou plusieurs sources de données. Idéalement, et pour respecter le principe de source unique de vérité ou Single Source of Truth en anglais, il convient de créer une classe de dépôt pour chaque type de données géré dans votre application.
Créez les dépôts AnimalsRepository
et NotesRepository
Dans le cadre de l’application PETiSoin, deux types de données sont gérés dans l’application : les animaux et les notes.
Pour respecter le principe de Single Source of Truth, il est donc nécessaire de créer deux classes de dépôts :AnimalsRepository
etNotesRepository
. Chacun de ces dépôts utilise un DAO comme source de données pour effectuer les opérations dans la base de données Room :AnimalDao
pour la classe de dépôtAnimalsRepository
etNoteDao
pour la classe de dépôtNotesRepository
.
Vous pouvez donc créer une classeAnimalsRepository
dans un package data.repository
et lui passer, au sein de son constructeur le DAOAnimalDao
.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
À ce stade, même si la structure est en place, ce n’est malheureusement pas suffisant pour indiquer à Hilt d’injecter le DAO dans la classe de dépôt. Pour le lui faire comprendre, il est nécessaire de compléter le code ci-dessus avec l'annotation @Inject
, au niveau du constructeur.
Si vous utilisez Java : | Si vous utilisez Kotlin, il convient d’utiliser explicitement le mot clé |
|
|
Définissez l’injection de la base de données
Puisque le DAO AnimalDao
est une interface, il est nécessaire de compléter la classeAppModule
afin d’indiquer à Hilt comment fournir les instances de ce DAO.
Dans le cas de l’application PETiSoin, le DAO vient de la base de données PETiSoinDatabase
. Vous pourriez donc utiliser la Singleton créée dans la partie précédente du cours pour récupérer le DAO. Mais puisqu’on utilise Hilt ici, vous pouvez également lui demander de se charger de maintenir le singleton de la base de données.
Dans la classeAppModule
, créez une méthode provideDatabase
qui renverra l’instance de la base de données.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Pour fournir la base de données, nous n’allons pas réutiliser l’implémentation du patron de conception Singleton mise en place précédemment. Nous allons repartir d’une feuille blanche et laisser Hilt gérer lui-même le Singleton. Vous allez donc créer vous-même l’instance de la base de données à l’aide des méthodes de Room et notamment la méthodedatabaseBuilder
.
Puisque vous fournissez vous-même l’instance, vous devez ajouter l’annotation
@Provides
sur la méthode.Et comme vous souhaitez appliquer le patron de conception Singleton sur la base de données pour limiter le nombre de connexion, l’annotation
@Singleton
doit également figurer.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
La méthodedatabaseBuilder
, qui permet de créer une instance de la base de données, nécessite unContext
en tant que paramètre. Comment en obtenir un ?
Pas de panique ! Hilt a tout prévu. Il est possible d’injecter unContext
dans la méthode en y ajoutant un attribut de typeContext
sur lequel l’annotation @ApplicationContext
est appliquée.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Il ne reste plus qu’à compléter le corps de la méthode avec la création de l’instance de la base de données.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Définissez l’injection du DAO
Maintenant qu'Hilt sait comment gérer l’instance de la base de données, il est possible de compléter la classeAppModule
pour lui indiquer comment injecter l’interface AnimalDao
dans la classeAnimalsRepository
.
1. Dans la classeAppModule
, créez une méthodeprovideAnimalDao
qui a pour paramètre une instance de la classePETiSoinDatabase
et qui renvoie, depuis cette instance, le DAO.
2. N’oubliez pas de faire porter l’annotation @Provides
à la méthode pour indiquer à Hilt que c’est bien une instance que vous fournissez.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
3. Finalement, il est possible de compléter la classeAnimalsRepository
avec les différentes opérations CRUD.
Si vous utilisez Java : | Si vous utilisez Kotlin : |
|
|
Voici une vidéo qui récapitule les principales étapes pour mettre en place la patron de conception Repository au niveau de la couche de données de l’architecture MVVM dans l’application PETiSoin.
À vous de jouer
Contexte
Il est temps de mettre en place les bonnes pratiques de développement de la section “santé” de l’application PETiSoin en débutant par la couche de données du patron de conception MVVM.
Consignes
Dans le projet, disponible sur GitHub (Java ou Kotlin), vous devez créer un dépôt VaccinesRepository
qui contient une méthode qui renvoie l’ensemble des vaccins pour un animal.
Vous n’allez pas mettre en place Hilt mais compléter l’implémentation qui est déjà en place.
Livrables
Vous pouvez dupliquer le projet GitHub pour modifier le code source du projet et fournir un projet dans l’ensemble des tests passe.
En résumé
L’injection de dépendances permet de rendre le code maintenable et évolutif.
Au lieu de créer les objets dont une classe a besoin à l'intérieur de celle-ci, on les instancie à l'extérieur et on les passe ensuite à la classe.
Le patron de conception Repository permet de respecter le principe de Single Source of Truth.
Le patron de conception MVVM est aujourd’hui l’architecture recommandée par Google dans le cadre du développement d’une application Android.
La couche de données du patron de conception MVVM inclut le patron de conception Repository et une source de données qui est la base de données Room.
Vous avez écrit la couche de données de l’application PETiSoin. Vous pouvez maintenant vous attaquer à la couche interface utilisateur pour restituer visuellement les données aux utilisateurs de l’application.