Introduction à l'injection de dépendances L'injection de dépendances (DI) est un modèle de conception utilisé pour implémenter l'inversion de contrôle (IoC) où le contrôle de la création et de la gestion des dépendances est transféré de l'application à une entité externe. Cela permet de créer un code plus modulaire, testable et maintenable. Il s'agit d'une technique où la responsabilité de la création d'objets est transférée à d'autres parties du code. Cela favorise un couplage lâche, rendant le code plus modulaire et plus facile à gérer. Les classes ont souvent besoin de références à d'autres classes pour fonctionner correctement. Par exemple, considérons une classe qui nécessite une classe . Ces classes nécessaires sont appelées dépendances. La classe a besoin d'une instance de la classe pour fonctionner. Library Book Library Book Il existe trois manières principales pour une classe d'obtenir les objets dont elle a besoin : : La classe crée et initialise ses propres dépendances. Par exemple, la classe créerait et initialiserait sa propre instance de la classe . Auto-construction Library Book : la classe récupère les dépendances à partir d'une source externe. Certaines API Android, telles que les getters et , fonctionnent de cette manière. Récupération externe Context getSystemService() : les dépendances sont fournies à la classe, soit lors de sa construction, soit via des méthodes qui les requièrent. Par exemple, le constructeur recevrait une instance en tant que paramètre. Injection de dépendances Library Book La troisième option est l'injection de dépendances ! Avec l'injection de dépendances, vous fournissez les dépendances d'une classe plutôt que de laisser l'instance de classe les obtenir elle-même. Exemple sans injection de dépendance Sans DI, une qui crée sa propre dépendance pourrait ressembler à ceci : Library Book class Library { private Book book = new Book(); void open() { book.read(); } } public class Main { public static void main(String[] args) { Library library = new Library(); library.open(); } } Il ne s'agit pas d'un exemple de DI car la classe construit son propre . Cela peut être problématique car : Library Book : et sont étroitement couplés. Une instance de utilise un type de , ce qui rend difficile l'utilisation de sous-classes ou d'implémentations alternatives. Couplage étroit Library Book Library Book : La dépendance stricte envers rend les tests plus difficiles. utilise une instance réelle de , ce qui empêche l'utilisation de doublons de test pour modifier pour différents cas de test. Difficultés de test Book Library Book Book Exemple avec injection de dépendance Avec DI, au lieu que chaque instance de construise son propre objet , elle reçoit un objet comme paramètre dans son constructeur : Library Book Book class Library { private Book book; Library(Book book) { this.book = book; } void open() { book.read(); } } public class Main { public static void main(String[] args) { Book book = new Book(); Library library = new Library(book); library.open(); } La fonction principale utilise . Étant donné que dépend de , l'application crée une instance de et l'utilise ensuite pour construire une instance de . Les avantages de cette approche basée sur l'injection de dépendances sont les suivants : Library Library Book Book Library : vous pouvez transmettre différentes implémentations de à . Par exemple, vous pouvez définir une nouvelle sous-classe de appelée que vous souhaitez que utilise. Avec DI, vous transmettez simplement une instance de à , et cela fonctionne sans aucune autre modification. Réutilisabilité de Library Book Library Book EBook Library EBook Library : vous pouvez passer des tests doublons pour tester différents scénarios. Test facile de Library Un autre exemple de DI Considérez un scénario dans lequel une classe s'appuie sur une classe . Sans DI, crée directement une instance de , ce qui rend difficile l'utilisation de différents types de notifications ou le test du service avec diverses implémentations de notifications. NotificationService Notification NotificationService Notification Pour illustrer DI, refactorisons cet exemple : interface Notification { void send(); } class EmailNotification implements Notification { @Override public void send() { // Send email notification } } class SMSNotification implements Notification { @Override public void send() { // Send SMS notification } } class NotificationService { void sendNotification(Notification notification) { notification.send(); } } Désormais, dépend de l'interface plutôt que d'une classe spécifique. Cela permet d'utiliser différentes implémentations de de manière interchangeable. Vous pouvez définir l'implémentation que vous souhaitez utiliser via la méthode : NotificationService Notification Notification sendNotification NotificationService service = new NotificationService(); service.sendNotification(new EmailNotification()); service.sendNotification(new SMSNotification()); Méthodes d'injection de dépendances dans Android Il existe trois principaux types de DI : : les dépendances sont transmises via des méthodes auxquelles la classe peut accéder via une interface ou une autre classe. L'exemple précédent illustre l'injection de méthode. Injection de méthode (interface) : les dépendances sont transmises à la classe via son constructeur. Injection de constructeur class NotificationService { private final Notification notification; public NotificationService(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(new EmailNotification()); service.sendNotification(); } } : certaines classes du framework Android, telles que les activités et les fragments, sont instanciées par le système, de sorte que l'injection de constructeur n'est pas possible. Avec l'injection de champ, les dépendances sont instanciées après la création de la classe. 3. Injection de champ (ou injection de setter) class NotificationService { private Notification notification; public Notification getNotification() { return notification; } public void setNotification(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(); service.setNotification(new EmailNotification()); service.sendNotification(); } } : les dépendances sont fournies via des méthodes, souvent à l'aide de l'annotation . 4. Injection de méthode @Inject Avantages de l'injection de dépendances Les classes deviennent plus réutilisables et moins dépendantes d'implémentations spécifiques. Cela est dû à l'inversion de contrôle, où les classes ne gèrent plus leurs dépendances mais fonctionnent avec n'importe quelle configuration fournie. Les dépendances font partie de la surface de l'API et peuvent être vérifiées lors de la création de l'objet ou au moment de la compilation, ce qui facilite le refactoring. Étant donné qu'une classe ne gère pas ses dépendances, différentes implémentations peuvent être transmises lors des tests pour couvrir différents scénarios. Injection de dépendances automatisée Dans l'exemple précédent, vous avez créé, fourni et géré manuellement les dépendances de différentes classes sans utiliser de bibliothèque. Cette approche est connue sous le nom d'injection de dépendances manuelle. Bien qu'elle fonctionne pour les cas simples, elle devient fastidieuse à mesure que le nombre de dépendances et de classes augmente. L'injection de dépendances manuelle présente plusieurs inconvénients : : pour les applications volumineuses, la gestion de toutes les dépendances et leur connexion correcte peuvent entraîner une grande quantité de code répétitif. Dans une architecture multicouche, la création d'un objet pour une couche supérieure nécessite de fournir toutes les dépendances pour les couches inférieures. Par exemple, pour construire un ordinateur, vous avez besoin d'un processeur, d'une carte mère, de RAM et d'autres composants ; et un processeur peut avoir besoin de transistors et de condensateurs. Code standard : lorsque vous ne pouvez pas créer de dépendances à l'avance (par exemple avec des initialisations paresseuses ou la définition d'objets dans des flux spécifiques de votre application), vous devez écrire et maintenir un conteneur personnalisé (ou un graphique de dépendances) pour gérer la durée de vie de vos dépendances en mémoire. Gestion des dépendances complexes Les bibliothèques peuvent automatiser ce processus en créant et en fournissant des dépendances pour vous. Ces bibliothèques se répartissent en deux catégories : : elles connectent les dépendances au moment de l’exécution. Solutions basées sur la réflexion : elles génèrent du code pour connecter les dépendances au moment de la compilation. Solutions statiques Dagger est une bibliothèque d'injection de dépendances populaire pour Java, Kotlin et Android, gérée par Google. Dagger simplifie l'injection de dépendances dans votre application en créant et en gérant le graphique de dépendances pour vous. Il fournit des dépendances entièrement statiques au moment de la compilation, résolvant de nombreux problèmes de développement et de performances associés aux solutions basées sur la réflexion comme Guice. Solutions basées sur la réflexion Ces frameworks connectent les dépendances au moment de l'exécution : : un framework d'exécution DI qui utilise la réflexion pour connecter les dépendances. Il est conçu pour être léger et rapide, ce qui le rend adapté aux applications Android. Toothpick Solutions statiques Ces frameworks génèrent du code pour connecter les dépendances au moment de la compilation : : Construit sur la base de Dagger, Hilt fournit un moyen standard d'intégrer l'injection de dépendances Dagger dans une application Android. . Hilt Il simplifie la configuration et l'utilisation de Dagger en fournissant des composants et des portées prédéfinis : un framework DI léger et simple pour Kotlin. Koin utilise un DSL pour définir les dépendances et est facile à configurer et à utiliser. Koin : un framework DI basé sur Kotlin, facile à utiliser et à comprendre. Il fournit une API simple et flexible pour la gestion des dépendances. Kodein Alternatives à l'injection de dépendances Une alternative à l'injection de dépendances est le modèle de localisateur de services. Ce modèle de conception permet également de découpler les classes de leurs dépendances concrètes. Vous créez une classe appelée localisateur de services qui crée et stocke les dépendances, en les fournissant à la demande. object ServiceLocator { fun getProcessor(): Processor = Processor() } class Computer { private val processor = ServiceLocator.getProcessor() fun start() { processor.run() } } fun main(args: Array<String>) { val computer = Computer() computer.start() } Le modèle de localisateur de services diffère de l'injection de dépendances dans la manière dont les dépendances sont consommées. Avec le modèle de localisateur de services, les classes demandent les dépendances dont elles ont besoin ; avec l'injection de dépendances, l'application fournit de manière proactive les objets requis. Qu'est-ce que Dagger 2 ? Dagger 2 est un framework DI populaire pour Android. Il utilise la génération de code au moment de la compilation et est connu pour ses hautes performances. Dagger 2 simplifie le processus d'injection de dépendances en générant le code nécessaire pour gérer les dépendances, en réduisant le code standard et en améliorant l'efficacité. Dagger 2 est une bibliothèque basée sur des annotations pour l'injection de dépendances dans Android. Voici les principales annotations et leurs objectifs : : utilisé pour définir des classes qui fournissent des dépendances. Par exemple, un module peut fournir un pour Retrofit. @Module ApiClient : annote les méthodes d'un module pour spécifier comment créer et renvoyer des dépendances. @Provides : utilisé pour demander des dépendances. Peut être appliqué aux champs, aux constructeurs et aux méthodes. @Inject : une interface qui relie et . Elle contient tous les modules et fournit le générateur pour l'application. @Component @Module @Inject : garantit qu'une seule instance d'une dépendance est créée. @Singleton : utilisé dans les classes abstraites pour fournir des dépendances, similaire à mais plus concis. @Binds @Provides Composants de la dague Dagger peut générer un graphique de dépendances pour votre projet, lui permettant de déterminer où obtenir les dépendances en cas de besoin. Pour cela, vous devez créer une interface et l'annoter avec . @Component Dans l'interface , vous définissez des méthodes qui renvoient des instances des classes dont vous avez besoin (par exemple, ). L'annotation demande à Dagger de générer un conteneur avec toutes les dépendances requises pour satisfaire les types qu'il expose. Ce conteneur est connu sous le nom de composant Dagger et il contient un graphique d'objets que Dagger sait fournir avec leurs dépendances. @Component BookRepository @Component Exemple Considérons un exemple impliquant un : LibraryRepository : ajoutez une annotation au constructeur afin que Dagger sache comment créer une instance de . Annoter le constructeur @Inject LibraryRepository LibraryRepository public class LibraryRepository { private final LocalLibraryDataSource localDataSource; private final RemoteLibraryDataSource remoteDataSource; @Inject public LibraryRepository(LocalLibraryDataSource localDataSource, RemoteLibraryDataSource remoteDataSource) { this.localDataSource = localDataSource; this.remoteDataSource = remoteDataSource; } } : De même, annotez les constructeurs des dépendances ( et ) afin que Dagger sache comment les créer. 2. Annoter les dépendances LocalLibraryDataSource RemoteLibraryDataSource public class LocalLibraryDataSource { @Inject public LocalLibraryDataSource() { // Initialization code } } public class RemoteLibraryDataSource { private final LibraryService libraryService; @Inject public RemoteLibraryDataSource(LibraryService libraryService) { this.libraryService = libraryService; } } : Créez une interface annotée avec pour définir le graphe de dépendances. 3. Définir le composant @Component @Component public interface ApplicationComponent { LibraryRepository getLibraryRepository(); } Lorsque vous générez le projet, Dagger génère pour vous une implémentation de l'interface , généralement nommée . ApplicationComponent DaggerApplicationComponent Usage Vous pouvez maintenant utiliser le composant généré pour obtenir des instances de vos classes avec leurs dépendances injectées automatiquement : public class MainApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.create(); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } } Dans votre activité ou fragment, vous pouvez récupérer l'instance : LibraryRepository public class MainActivity extends AppCompatActivity { @Inject LibraryRepository libraryRepository; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MainApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected libraryRepository } } Concepts clés de Dagger 2 1. Modules ∘ Concepts clés des modules ∘ Inclure des modules dans les composants 2. Portées 3. Composants 4. Dépendances des composants 5. Liaisons d'exécution 1. Modules Les modules de Dagger 2 sont des classes annotées avec qui fournissent des dépendances aux composants. Ils contiennent des méthodes annotées avec ou pour spécifier comment créer et fournir des dépendances. Les modules sont essentiels pour organiser et gérer la création des objets dont votre application a besoin. @Module @Provides @Binds Concepts clés des modules @Module Annotation : cette annotation permet de définir une classe comme un module Dagger. Une classe de module contient des méthodes qui fournissent des dépendances. @Provides Annotation : cette annotation est utilisée sur les méthodes d'un module pour indiquer que la méthode fournit une certaine dépendance. Ces méthodes sont responsables de la création et du renvoi des instances des dépendances. Annotation @Binds : cette annotation est utilisée dans les classes abstraites pour lier une implémentation à une interface. Elle est plus concise que et est utilisée lorsque le module est une classe abstraite. @Provides Exemple de module @Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } @Provides @Singleton OkHttpClient provideOkHttpClient() { return new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build(); } } Dans cet exemple, est une classe annotée avec . Elle contient deux méthodes annotées avec qui créent et renvoient des instances de et . NetworkModule @Module @Provides Retrofit OkHttpClient Utilisation de @Binds Lorsque vous disposez d'une interface et de son implémentation, vous pouvez utiliser pour lier l'implémentation à l'interface. C'est plus concis que d'utiliser . @Binds @Provides public interface ApiService { void fetchData(); } public class ApiServiceImpl implements ApiService { @Override public void fetchData() { // Implementation } } @Module public abstract class ApiModule { @Binds abstract ApiService bindApiService(ApiServiceImpl apiServiceImpl); } Dans cet exemple, est une classe abstraite annotée avec . La méthode est annotée avec pour lier à . ApiModule @Module bindApiService @Binds ApiServiceImpl ApiService Les modules peuvent être organisés en fonction des fonctionnalités qu'ils fournissent. Par exemple, vous pouvez disposer de modules distincts pour les opérations réseau, les opérations de base de données et les dépendances liées à l'interface utilisateur. Exemple: : fournit des dépendances liées au réseau telles que et . NetworkModule Retrofit OkHttpClient : fournit des dépendances liées à la base de données comme . DatabaseModule RoomDatabase : fournit des dépendances liées à l'interface utilisateur telles que et . UIModule ViewModel Presenter Inclure des modules dans les composants Les modules sont inclus dans les composants pour fournir des dépendances aux classes qui en ont besoin. Voici comment vous pouvez le configurer : ApplicationComponent.java : @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); } Dans cet exemple, inclut et pour fournir des dépendances à l'application. ApplicationComponent NetworkModule DatabaseModule 2. Portées Les portées dans Dagger 2 sont des annotations qui définissent le cycle de vie des dépendances. Elles garantissent qu'une seule instance d'une dépendance est créée et partagée dans une portée spécifiée. Cela permet de gérer efficacement la mémoire et de garantir que les dépendances sont réutilisées le cas échéant. : garantit une instance unique d'une dépendance tout au long du cycle de vie de l'application. Portée Singleton : garantit une instance unique d'une dépendance dans le cycle de vie d'une activité. Portée de l'activité : garantit une instance unique d’une dépendance dans le cycle de vie d’un fragment. Portée du fragment 1. Portée Singleton : La portée garantit qu'une seule instance d'une dépendance est créée et partagée tout au long du cycle de vie de l'application. Définition @Singleton Cette portée est généralement utilisée pour les dépendances qui doivent être partagées dans l'ensemble de l'application, telles que les clients réseau, les instances de base de données ou les préférences partagées. Exemple: @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); } Dans cet exemple, l'annotation garantit que les instances et fournies par et sont des singletons et partagées dans l'ensemble de l'application. @Singleton Retrofit Database NetworkModule DatabaseModule 2. Champ d'activité : (une portée personnalisée) garantit qu'une seule instance d'une dépendance est créée et partagée au cours du cycle de vie d'une activité. Définition @ActivityScope Cette portée est utile pour les dépendances spécifiques à une activité et doivent être recréées à chaque recréation de l'activité, comme les présentateurs ou les modèles de vue. : Exemple @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); } Dans cet exemple, l'annotation garantit que les dépendances fournies par sont limitées au cycle de vie de l'activité. @ActivityScope ActivityModule 3. Portée du fragment : (une autre portée personnalisée) garantit qu'une seule instance d'une dépendance est créée et partagée dans le cycle de vie d'un fragment. Définition @FragmentScope Cas d'utilisation : cette portée est utile pour les dépendances spécifiques à un fragment et doivent être recréées à chaque recréation du fragment, comme les présentateurs ou les modèles de vue spécifiques à un fragment. : Exemple @Scope @Retention(RetentionPolicy.RUNTIME) public @interface FragmentScope { } @FragmentScope @Component(dependencies = ActivityComponent.class, modules = FragmentModule.class) public interface FragmentComponent { void inject(MyFragment myFragment); } Dans cet exemple, l'annotation garantit que les dépendances fournies par sont limitées au cycle de vie du fragment. @FragmentScope FragmentModule 3. Composants Les dépendances entre composants permettent à un composant de dépendre d'un autre, ce qui permet la réutilisation des dépendances. Il existe deux principaux types de dépendances entre composants : : fournit les dépendances nécessaires à l'ensemble de l'application. Composant d'application : fournit les dépendances nécessaires à une activité spécifique. Composant d'activité 1. Composant d'application : Le composant d'application fournit des dépendances nécessaires à l'ensemble de l'application. Il est généralement limité à pour garantir que les dépendances sont partagées dans toute l'application. Définition @Singleton Ce composant est utilisé pour les dépendances qui doivent être disponibles à l'échelle mondiale, telles que les clients réseau, les instances de base de données ou les préférences partagées. : Exemple @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); } Dans cet exemple, est responsable de la fourniture des instances et , qui sont partagées dans l'ensemble de l'application. ApplicationComponent Retrofit Database 2. Composante d'activité : Le composant Activity fournit les dépendances nécessaires à une activité spécifique. Il est généralement défini avec une portée personnalisée, telle que , pour garantir que les dépendances sont recréées à chaque fois que l'activité est recréée. Définition @ActivityScope Ce composant est utilisé pour les dépendances spécifiques à une activité, telles que les présentateurs ou les modèles de vue. : Exemple @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); } Dans cet exemple, dépend d' et fournit des dépendances spécifiques à . ActivityComponent ApplicationComponent MainActivity 4. Dépendances des composants Les dépendances entre composants permettent à un composant de dépendre d'un autre, ce qui permet la réutilisation des dépendances. Il existe deux principaux types de dépendances entre composants : : Un sous-composant est un enfant d'un autre composant et peut accéder aux dépendances de son parent. Sous-composants : cela permet à un composant de dépendre d'un autre composant sans être un sous-composant. Attribut de dépendance 1. Sous-composants : Un sous-composant est un enfant d'un autre composant et peut accéder aux dépendances de son parent. Les sous-composants sont définis dans le composant parent et peuvent hériter de sa portée. : Exemple @ActivityScope @Subcomponent(modules = ActivityModule.class) public interface ActivitySubcomponent { void inject(MainActivity mainActivity); } Dans cet exemple, est un sous-composant du composant parent et peut accéder à ses dépendances. ActivitySubcomponent 2. Attribut de dépendance Cela permet à un composant de dépendre d'un autre composant sans être un sous-composant. Le composant dépendant peut accéder aux dépendances fournies par le composant parent. : Exemple @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); } Dans cet exemple, dépend d' et peut accéder à ses dépendances. ActivityComponent ApplicationComponent 5. Liaisons d'exécution Les liaisons d'exécution dans Dagger 2 font référence à la fourniture de dépendances créées et gérées au moment de l'exécution, en fonction du contexte dans lequel elles sont nécessaires. : utilisé pour les dépendances qui doivent vivre aussi longtemps que l'application. Contexte d'application : utilisé pour les dépendances qui doivent vivre aussi longtemps qu'une activité. Contexte d'activité 1. Contexte d'application : Le contexte applicatif est un contexte lié au cycle de vie de l'application dans son ensemble. Il est utilisé pour les dépendances qui doivent vivre aussi longtemps que l'application elle-même. Définition Dépendances partagées dans l'ensemble de l'application et qui n'ont pas besoin d'être recréées pour chaque activité ou fragment. Les exemples incluent les clients réseau, les instances de base de données et les préférences partagées. : Exemple @Module public class AppModule { private final Application application; public AppModule(Application application) { this.application = application; } @Provides @Singleton Application provideApplication() { return application; } @Provides @Singleton Context provideApplicationContext() { return application.getApplicationContext(); } } Dans cet exemple, fournit le contexte de l'application sous forme de dépendance singleton. La méthode garantit que le contexte fourni est lié au cycle de vie de l'application. AppModule provideApplicationContext 2. Contexte de l'activité : Le contexte d'activité est un contexte lié au cycle de vie d'une activité spécifique. Il est utilisé pour les dépendances qui doivent vivre aussi longtemps que l'activité elle-même. Définition Dépendances spécifiques à une activité et devant être recréées à chaque fois que l'activité est recréée. Exemples : modèles de vue, présentateurs et dépendances liées à l'interface utilisateur. : Exemple @Module public class ActivityModule { private final Activity activity; public ActivityModule(Activity activity) { this.activity = activity; } @Provides @ActivityScope Activity provideActivity() { return activity; } @Provides @ActivityScope Context provideActivityContext() { return activity; } } Dans cet exemple, fournit le contexte d'activité sous forme de dépendance limitée. La méthode garantit que le contexte fourni est lié au cycle de vie de l'activité. ActivityModule provideActivityContext Utilisation des liaisons d'exécution dans les composants Pour utiliser ces liaisons d'exécution, vous devez inclure les modules correspondants dans vos composants : : Composant d'application @Singleton @Component(modules = {AppModule.class, NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); Context getApplicationContext(); } : Composante de l'activité @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); Context getActivityContext(); } Injection de contextes Une fois que vous avez configuré vos composants et modules, vous pouvez injecter les contextes dans vos classes selon vos besoins. : Exemple dans une activité public class MainActivity extends AppCompatActivity { @Inject Context activityContext; @Inject Context applicationContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ApplicationComponent appComponent = ((MyApplication) getApplication()).getApplicationComponent(); ActivityComponent activityComponent = DaggerActivityComponent.builder() .applicationComponent(appComponent) .activityModule(new ActivityModule(this)) .build(); activityComponent.inject(this); // Use the injected contexts Log.d("MainActivity", "Activity Context: " + activityContext); Log.d("MainActivity", "Application Context: " + applicationContext); } } Dans cet exemple, reçoit à la fois le contexte d'activité et le contexte d'application via l'injection de dépendances. Cela permet à l'activité d'utiliser le contexte approprié en fonction des besoins spécifiques des dépendances. MainActivity Exemple : Utilisation de Dagger 2 dans une application Android Configuration de Dagger 2 Pour utiliser Dagger 2 dans votre projet, vous devez ajouter les dépendances suivantes à votre fichier : build.gradle dependencies { implementation 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' } Remplacez par la dernière version de Dagger 2. 2.x Étape 1 : Définir un module Créez un module pour fournir des dépendances. Par exemple, un pour fournir une instance : NetworkModule Retrofit @Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } } Étape 2 : Définir un composant Créez un composant pour relier le module et les classes qui ont besoin des dépendances : @Singleton @Component(modules = {NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); } Étape 3 : Injecter les dépendances Utilisez le composant pour injecter des dépendances dans vos classes. Par exemple, dans votre classe : Application public class MyApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .networkModule(new NetworkModule()) .build(); applicationComponent.inject(this); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } } Étape 4 : utiliser les dépendances injectées Vous pouvez maintenant utiliser les dépendances injectées dans vos classes. Par exemple, dans une : Activity public class MainActivity extends AppCompatActivity { @Inject Retrofit retrofit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MyApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected Retrofit instance // ... } } Conclusion Résumons ce sujet : L’objectif principal de DI est de relâcher le couplage, facilitant ainsi la gestion des dépendances. En utilisant DI, vous pouvez augmenter la flexibilité du code et simplifier le processus de test. DI est un sujet complexe avec différentes implémentations en fonction du scénario. La DI dans différentes langues présente des particularités qui peuvent affecter la façon dont vous travaillez avec elle. Dagger 2 automatise le processus de création et de fourniture de dépendances, réduisant ainsi le code standard et améliorant la maintenabilité. Dagger 2 offre une sécurité au moment de la compilation, garantissant que toutes les dépendances sont satisfaites avant l'exécution de l'application. En générant du code au moment de la compilation, Dagger 2 évite les frais de performances associés aux solutions basées sur la réflexion.