Injection de dépendances avec Hilt

Hilt est une bibliothèque d'injection de dépendances pour Android qui permet de réduire le code récurrent de l'injection de dépendances manuelle dans votre projet. L'injection de dépendances manuelle nécessite la création manuelle de chaque classe et de ses dépendances, ainsi que l'utilisation de conteneurs pour réutiliser et gérer les dépendances.

Hilt offre une méthode standard pour utiliser l'injection de dépendances dans votre application en fournissant des conteneurs pour chaque classe Android de votre projet et en gérant automatiquement leur cycle de vie. Hilt repose sur la bibliothèque d'injection de dépendances Dagger populaire et bénéficie ainsi de l'exactitude du temps de compilation, des performances d'exécution, de l'évolutivité et de la compatibilité avec Android Studio qu'offre Dagger. Pour en savoir plus, consultez la section Hilt et Dagger.

Ce guide décrit les concepts de base de Hilt et de ses conteneurs générés. Il comprend également une démonstration de l'amorçage d'une application existante pour utiliser Hilt.

Ajouter des dépendances

Tout d'abord, ajoutez le plug-in hilt-android-gradle-plugin au fichier build.gradle racine de votre projet :

Groovy

plugins {
  ...
  id 'com.google.dagger.hilt.android' version '2.44' apply false
}

Kotlin

plugins {
  ...
  id("com.google.dagger.hilt.android") version "2.44" apply false
}

Ensuite, appliquez le plug-in Gradle et ajoutez ces dépendances dans votre fichier app/build.gradle :

Groovy

...
plugins {
  id 'kotlin-kapt'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.44"
  kapt "com.google.dagger:hilt-compiler:2.44"
}

// Allow references to generated code
kapt {
  correctErrorTypes true
}

Kotlin

plugins {
  kotlin("kapt")
  id("com.google.dagger.hilt.android")
}

android {
  ...
}

dependencies {
  implementation("com.google.dagger:hilt-android:2.44")
  kapt("com.google.dagger:hilt-android-compiler:2.44")
}

// Allow references to generated code
kapt {
  correctErrorTypes = true
}

Hilt utilise les fonctionnalités de Java 8. Pour activer Java 8 dans votre projet, ajoutez les éléments suivants au fichier app/build.gradle :

Groovy

android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Kotlin

android {
  ...
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
  }
}

Classe d'application Hilt

Toutes les applications qui utilisent Hilt doivent contenir une classe Application annotée avec @HiltAndroidApp.

@HiltAndroidApp 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.

Kotlin

@HiltAndroidApp
class ExampleApplication : Application() { ... }

Java

@HiltAndroidApp
public class ExampleApplication extends Application { ... }

Ce composant Hilt généré est associé au cycle de vie de l'objet Application et lui fournit des dépendances. En outre, il s'agit du composant parent de l'application, ce qui signifie que d'autres composants peuvent accéder aux dépendances qu'il fournit.

Injecter des dépendances dans des classes Android

Une fois que Hilt est configuré dans votre classe Application et qu'un composant au niveau de l'application est disponible, Hilt peut fournir des dépendances à d'autres classes Android comportant l'annotation @AndroidEntryPoint :

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity { ... }

Hilt est actuellement compatible avec les classes Android suivantes :

  • Application (en utilisant @HiltAndroidApp)
  • ViewModel (en utilisant @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Si vous annotez une classe Android avec @AndroidEntryPoint, vous devez également annoter les classes Android qui en dépendent. Par exemple, si vous annotez un fragment, vous devez également annoter toutes les activités dans lesquelles vous l'utilisez.

@AndroidEntryPoint génère un composant Hilt individuel pour chaque classe Android de votre projet. Ces composants peuvent recevoir des dépendances provenant de leurs classes parentes respectives, comme décrit dans la section Hiérarchie des composants.

Pour obtenir des dépendances à partir d'un composant, utilisez l'annotation @Inject pour effectuer une injection de champ :

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Les classes injectées par Hilt peuvent avoir d'autres classes de base qui utilisent également l'injection. Ces classes ne nécessitent pas l'annotation @AndroidEntryPoint si elles sont abstraites.

Pour en savoir plus sur le rappel de cycle de vie auquel une classe Android est injectée, consultez la section Cycles de vie des composants.

Définir les liaisons de Hilt

Pour effectuer l'injection de champ, Hilt doit savoir comment fournir les instances des dépendances nécessaires à partir du composant correspondant. Une liaison contient les informations nécessaires pour fournir des instances d'un type en tant que dépendance.

L'une des méthodes permettant de fournir des informations de liaison sur Hilt est l'injection de constructeur. Utilisez l'annotation @Inject sur le constructeur d'une classe pour indiquer à Hilt comment fournir des instances de cette classe :

Kotlin

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

Java

public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

Les paramètres d'un constructeur annoté d'une classe sont les dépendances de cette classe. Dans l'exemple, AnalyticsAdapter utilise AnalyticsService comme dépendance. Par conséquent, Hilt doit également savoir comment fournir des instances de AnalyticsService.

Modules Hilt

Parfois, un constructeur ne peut pas être injecté dans un type. Les raisons peuvent être multiples. Par exemple, vous ne pouvez pas injecter un constructeur dans une interface. Vous ne pouvez pas non plus injecter un constructeur dans un type qui ne vous appartient pas, comme une classe provenant d'une bibliothèque externe. Dans ces cas, vous pouvez fournir des informations de liaison à Hilt à l'aide de modules Hilt.

Un module Hilt est une classe annotée avec @Module. Tout comme un module Dagger, il indique à Hilt comment fournir des instances de certains types. Contrairement aux modules Dagger, vous devez annoter les modules Hilt avec @InstallIn pour indiquer à Hilt la classe Android dans laquelle chaque module sera utilisé ou installé.

Les dépendances que vous fournissez dans les modules Hilt sont disponibles dans tous les composants générés associés à la classe Android où vous installez le module Hilt.

Injecter des instances d'interface avec "@Binds"

Prenons l'exemple d'AnalyticsService. Si AnalyticsService est une interface, vous ne pouvez pas y injecter un constructeur. Fournissez plutôt les informations de liaison à Hilt en créant une fonction abstraite annotée avec @Binds dans un module Hilt.

L'annotation @Binds indique à Hilt l'implémentation à utiliser lorsqu'il doit fournir une instance d'une interface.

La fonction annotée fournit les informations suivantes à Hilt :

  • Le type renvoyé de la fonction indique à Hilt l'interface dont la fonction fournit des instances.
  • Le paramètre de fonction indique à Hilt l'implémentation à fournir.

Kotlin

interface AnalyticsService {
  fun analyticsMethods()
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
  ...
) : AnalyticsService { ... }

@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {

  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

Java

public interface AnalyticsService {
  void analyticsMethods();
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
public class AnalyticsServiceImpl implements AnalyticsService {
  ...
  @Inject
  AnalyticsServiceImpl(...) {
    ...
  }
}

@Module
@InstallIn(ActivityComponent.class)
public abstract class AnalyticsModule {

  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

Le module Hilt AnalyticsModule est annoté avec @InstallIn(ActivityComponent::class), car vous souhaitez que Hilt injecte cette dépendance dans ExampleActivity. Cette annotation signifie que toutes les dépendances de AnalyticsModule sont disponibles dans toutes les activités de l'application.

Injecter des instances avec "@Provides"

Les interfaces ne sont pas le seul cas dans lequel vous ne pouvez pas injecter un constructeur dans un type. Il est également impossible d'injecter un constructeur si vous ne possédez pas la classe, car elle provient d'une bibliothèque externe (classes comme Retrofit, OkHttpClient ou les bases de données Room), ou si des instances doivent être créées avec le modèle de compilateur.

Prenons l'exemple précédent. Si vous ne possédez pas directement la classe AnalyticsService, vous pouvez indiquer à Hilt comment fournir des instances de ce type en créant une fonction dans un module Hilt et en l'annotant avec @Provides.

La fonction annotée fournit les informations suivantes à Hilt :

  • Le type renvoyé de la fonction indique à Hilt le type dont la fonction fournit des instances.
  • Les paramètres de la fonction indiquent à Hilt les dépendances du type correspondant.
  • Le corps de la fonction indique à Hilt comment fournir une instance du type correspondant. Hilt exécute le corps de la fonction chaque fois qu'il doit fournir une instance de ce type.

Kotlin

@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    // Potential dependencies of this type
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
  }
}

Java

@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    // Potential dependencies of this type
  ) {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

Fournir plusieurs liaisons pour le même type

Si vous avez besoin que Hilt fournisse différentes implémentations d'un même type en tant que dépendances, vous devez lui fournir plusieurs liaisons. Vous pouvez définir plusieurs liaisons pour le même type à l'aide de qualificatifs.

Un qualificatif est une annotation qui permet d'identifier une liaison spécifique d'un type lorsque plusieurs liaisons sont définies pour ce type.

Prenons cet exemple. Si vous devez intercepter les appels vers AnalyticsService, vous pouvez utiliser un objet OkHttpClient avec un intercepteur. Pour les autres services, vous devrez peut-être intercepter les appels d'une autre manière. Dans ce cas, vous devez indiquer à Hilt comment fournir deux implémentations différentes de OkHttpClient.

Tout d'abord, définissez les qualificatifs que vous utiliserez pour annoter les méthodes @Binds ou @Provides :

Kotlin

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient

Java

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface AuthInterceptorOkHttpClient {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface OtherInterceptorOkHttpClient {}

Ensuite, Hilt doit savoir comment fournir une instance du type correspondant à chaque qualificatif. Dans ce cas, vous pouvez utiliser un module Hilt avec @Provides. Les deux méthodes ont le même type renvoyé, mais les qualificatifs les associent à deux liaisons différentes :

Kotlin

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  fun provideAuthInterceptorOkHttpClient(
    authInterceptor: AuthInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(authInterceptor)
               .build()
  }

  @OtherInterceptorOkHttpClient
  @Provides
  fun provideOtherInterceptorOkHttpClient(
    otherInterceptor: OtherInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(otherInterceptor)
               .build()
  }
}

Java

@Module
@InstallIn(ActivityComponent.class)
public class NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideAuthInterceptorOkHttpClient(
    AuthInterceptor authInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(authInterceptor)
                   .build();
  }

  @OtherInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideOtherInterceptorOkHttpClient(
    OtherInterceptor otherInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(otherInterceptor)
                   .build();
  }
}

Vous pouvez injecter le type spécifique dont vous avez besoin en annotant le champ ou le paramètre avec le qualificatif correspondant :

Kotlin

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .client(okHttpClient)
               .build()
               .create(AnalyticsService::class.java)
  }
}

// As a dependency of a constructor-injected class.
class ExampleServiceImpl @Inject constructor(
  @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
) : ...

// At field injection.
@AndroidEntryPoint
class ExampleActivity: AppCompatActivity() {

  @AuthInterceptorOkHttpClient
  @Inject lateinit var okHttpClient: OkHttpClient
}

Java

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    @AuthInterceptorOkHttpClient OkHttpClient okHttpClient
  ) {
      return new Retrofit.Builder()
                  .baseUrl("https://example.com")
                  .client(okHttpClient)
                  .build()
                  .create(AnalyticsService.class);
  }
}

// As a dependency of a constructor-injected class.
public class ExampleServiceImpl ... {

  private final OkHttpClient okHttpClient;

  @Inject
  ExampleServiceImpl(@AuthInterceptorOkHttpClient OkHttpClient okHttpClient) {
    this.okHttpClient = okHttpClient;
  }
}

// At field injection.
@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @AuthInterceptorOkHttpClient
  @Inject
  OkHttpClient okHttpClient;
  ...
}

Si vous ajoutez un qualificatif à un type, nous vous recommandons d'ajouter des qualificatifs à toutes les méthodes possibles permettant de fournir cette dépendance. Une implémentation de base ou commune sans qualificatif risque d'entraîner des erreurs et pourrait provoquer l'injection d'une mauvaise dépendance par Hilt.

Qualificatifs prédéfinis dans Hilt

Hilt fournit quelques qualificatifs prédéfinis. Par exemple, comme vous aurez peut-être besoin de la classe Context de l'application ou de l'activité, Hilt fournit les qualificatifs @ApplicationContext et @ActivityContext.

Supposons que la classe AnalyticsAdapter de l'exemple ait besoin du contexte de l'activité. Le code suivant montre comment fournir le contexte de l'activité à AnalyticsAdapter :

Kotlin

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }

Java

public class AnalyticsAdapter {

  private final Context context;
  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(
    @ActivityContext Context context,
    AnalyticsService service
  ) {
    this.context = context;
    this.service = service;
  }
}

Pour les autres liaisons prédéfinies disponibles dans Hilt, consultez la section Liaisons par défaut des composants.

Composants générés pour les classes Android

Pour chaque classe Android dans laquelle vous pouvez effectuer une injection de champ, un composant Hilt associé est disponible dans l'annotation @InstallIn. Chaque composant Hilt est chargé d'injecter ses liaisons dans la classe Android correspondante.

Les exemples précédents ont illustré l'utilisation d'ActivityComponent dans les modules Hilt.

Hilt fournit les composants suivants :

Composant Hilt Injecteur pour
SingletonComponent Application
ActivityRetainedComponent N/A
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View annoté avec @WithFragmentBindings
ServiceComponent Service

Cycles de vie des composants

Hilt crée et détruit automatiquement des instances de classes de composants générés en suivant le cycle de vie des classes Android correspondantes.

Composant généré Créé dans Détruit dans
SingletonComponent Application#onCreate() Application détruite
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel créé ViewModel détruit
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View détruite
ViewWithFragmentComponent View#super() View détruite
ServiceComponent Service#onCreate() Service#onDestroy()

Champs d'application des composants

Par défaut, toutes les liaisons dans Hilt n'ont pas de champ d'application. Cela signifie que, chaque fois que votre application demande la liaison, Hilt crée une instance du type requis.

Dans l'exemple, chaque fois que Hilt fournit AnalyticsAdapter en tant que dépendance d'un autre type ou via l'injection de champ (comme dans ExampleActivity), Hilt fournit une nouvelle instance d'AnalyticsAdapter.

Toutefois, Hilt permet également de définir le champ d'application d'une liaison sur un composant particulier. Hilt crée un champ d'application de liaison une seule fois par instance du composant sur lequel le champ d'application de liaison est défini. Toutes les demandes pour cette liaison partagent la même instance.

Le tableau ci-dessous indique les annotations de champ d'application pour chaque composant généré :

Classe Android Composant généré Champ d'application
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View annoté avec @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

Dans l'exemple, si vous définissez le champ d'application d'AnalyticsAdapter sur l'ActivityComponent à l'aide d'@ActivityScoped, Hilt fournit la même instance d'AnalyticsAdapter tout au long du cycle de vie de l'activité correspondante :

Kotlin

@ActivityScoped
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

Java

@ActivityScoped
public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

Supposons qu'AnalyticsService ait un état interne nécessitant que la même instance soit utilisée à chaque fois, non seulement dans ExampleActivity, mais aussi dans toute l'application. Dans ce cas, il est approprié de définir le champ d'application d'AnalyticsService sur SingletonComponent. Par conséquent, lorsque le composant doit fournir une instance d'AnalyticsService, il fournit la même instance à chaque fois.

L'exemple suivant montre comment définir le champ d'application d'une liaison sur un composant d'un module Hilt. Le champ d'application d'une liaison doit correspondre à celui du composant où elle est installée. Dans cet exemple, vous devez donc installer AnalyticsService dans SingletonComponent au lieu d'ActivityComponent :

Kotlin

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

// If you don't own AnalyticsService.
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {

  @Singleton
  @Provides
  fun provideAnalyticsService(): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
  }
}

Java

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

// If you don't own AnalyticsService.
@Module
@InstallIn(SingletonComponent.class)
public class AnalyticsModule {

  @Singleton
  @Provides
  public static AnalyticsService provideAnalyticsService() {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

Pour en savoir plus sur les champs d'application des composants Hilt, consultez la page Définition du champ d'application dans Android et Hilt.

Hiérarchie des composants

L'installation d'un module dans un composant permet d'accéder à ses liaisons en tant que dépendance d'autres liaisons dans ce composant ou dans tout composant enfant inférieur dans la hiérarchie des composants :

"ViewWithFragmentComponent" se trouve sous "FragmentComponent". "FragmentComponent" et "ViewComponent" se trouvent sous "ActivityComponent". "ActivityComponent" se trouve sous "ActivityRetainedComponent". "ViewModelComponent" se trouve sous "ActivityRetainedComponent". "ActivityRetainedComponent" et "ServiceComponent" se trouvent sous "SingletonComponent".
Figure 1. Hiérarchie des composants générés par Hilt.

Liaisons par défaut des composants

Chaque composant Hilt est fourni avec un ensemble de liaisons par défaut que Hilt peut injecter en tant que dépendances dans vos propres liaisons personnalisées. Notez que ces liaisons correspondent à des types d'activité et de fragments généraux, et non à une sous-classe spécifique. En effet, Hilt utilise une seule définition de composant d'activité pour injecter toutes les activités. Chaque activité est associée à une instance différente de ce composant.

Composant Android Liaisons par défaut
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application, Activity
FragmentComponent Application, Activity, Fragment
ViewComponent Application, Activity, View
ViewWithFragmentComponent Application, Activity, Fragment, View
ServiceComponent Application, Service

La liaison de contexte de l'application est également disponible à l'aide de @ApplicationContext. Exemple :

Kotlin

class AnalyticsServiceImpl @Inject constructor(
  @ApplicationContext context: Context
) : AnalyticsService { ... }

// The Application binding is available without qualifiers.
class AnalyticsServiceImpl @Inject constructor(
  application: Application
) : AnalyticsService { ... }

Java

public class AnalyticsServiceImpl implements AnalyticsService {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ApplicationContext Context context) {
    this.context = context;
  }
}

// The Application binding is available without qualifiers.
public class AnalyticsServiceImpl implements AnalyticsService {

  private final Application application;

  @Inject
  AnalyticsAdapter(Application application) {
    this.application = application;
  }
}

La liaison de contexte de l'activité est également disponible à l'aide de @ActivityContext. Exemple :

Kotlin

class AnalyticsAdapter @Inject constructor(
  @ActivityContext context: Context
) { ... }

// The Activity binding is available without qualifiers.
class AnalyticsAdapter @Inject constructor(
  activity: FragmentActivity
) { ... }

Java

public class AnalyticsAdapter {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ActivityContext Context context) {
    this.context = context;
  }
}

// The Activity binding is available without qualifiers.
public class AnalyticsAdapter {

  private final FragmentActivity activity;

  @Inject
  AnalyticsAdapter(FragmentActivity activity) {
    this.activity = activity;
  }
}

Injecter des dépendances dans des classes non compatibles avec Hilt

Hilt est compatible avec les classes Android les plus courantes. Cependant, vous devrez peut-être effectuer une injection de champ dans les classes non compatibles avec Hilt.

Dans ces cas, vous pouvez créer un point d'entrée à l'aide de l'annotation @EntryPoint. Un point d'entrée est la limite entre le code géré par Hilt et le code qui ne l'est pas. Il s'agit du point au niveau duquel le code entre pour la première fois dans le graphique des objets gérés par Hilt. Les points d'entrée permettent à Hilt d'utiliser du code qu'il ne gère pas pour fournir des dépendances dans le graphique de dépendances.

Par exemple, Hilt ne prend pas directement en charge les fournisseurs de contenu. Si vous souhaitez qu'un fournisseur de contenu utilise Hilt pour obtenir des dépendances, vous devez définir une interface annotée avec @EntryPoint pour chaque type de liaison souhaité et inclure des qualificatifs. Ajoutez ensuite @InstallIn pour spécifier le composant dans lequel installer le point d'entrée, comme suit :

Kotlin

class ExampleContentProvider : ContentProvider() {

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface ExampleContentProviderEntryPoint {
    fun analyticsService(): AnalyticsService
  }

  ...
}

Java

public class ExampleContentProvider extends ContentProvider {

  @EntryPoint
  @InstallIn(SingletonComponent.class)
  interface ExampleContentProviderEntryPoint {
    public AnalyticsService analyticsService();
  }
  ...
}

Pour accéder à un point d'entrée, utilisez la méthode statique appropriée depuis EntryPointAccessors. Le paramètre doit correspondre soit à l'instance du composant, soit à l'objet @AndroidEntryPoint qui sert de conteneur de composants. Assurez-vous que le composant transmis en tant que paramètre et la méthode statique EntryPointAccessors correspondent tous deux à la classe Android dans l'annotation @InstallIn sur l'interface @EntryPoint :

Kotlin

class ExampleContentProvider: ContentProvider() {
    ...

  override fun query(...): Cursor {
    val appContext = context?.applicationContext ?: throw IllegalStateException()
    val hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java)

    val analyticsService = hiltEntryPoint.analyticsService()
    ...
  }
}

Java

public class ExampleContentProvider extends ContentProvider {

  @Override
  public Cursor query(...) {
    Context appContext = getContext().getApplicationContext();
    ExampleContentProviderEntryPoint hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class);
    AnalyticsService analyticsService = hiltEntryPoint.analyticsService();
  }
}

Dans cet exemple, vous devez utiliser l'ApplicationContext pour récupérer le point d'entrée, car il est installé dans SingletonComponent. Si la liaison que vous souhaitez récupérer se trouve dans ActivityComponent, utilisez plutôt l'ActivityContext.

Hilt et Dagger

Hilt repose sur la bibliothèque d'injection de dépendances Dagger et permet d'intégrer Dagger à une application Android de manière standard.

En ce qui concerne Dagger, les objectifs de Hilt sont les suivants :

  • Simplifier l'infrastructure liée à Dagger pour les applications Android.
  • Créer un ensemble standard de composants et de champs d'application pour faciliter la configuration, la lisibilité et le partage de code entre les applications.
  • Offrir un moyen simple de fournir différentes liaisons pour divers types de compilation, tels que des tests, des débogages ou des versions.

Étant donné que le système d'exploitation Android instancie un grand nombre de ses propres classes de framework, l'utilisation de Dagger dans une application Android nécessite une quantité importante de code récurrent. Hilt réduit le code récurrent impliqué dans l'utilisation de Dagger dans une application Android. Hilt génère et fournit automatiquement les éléments suivants :

  • Des composants permettant d'intégrer des classes du framework Android avec Dagger, qu'il faudrait sinon créer manuellement.
  • Des annotations de champ d'application à utiliser avec les composants générés automatiquement par Hilt.
  • Des liaisons prédéfinies pour représenter des classes Android telles que Application ou Activity.
  • Des qualificatifs prédéfinis pour représenter @ApplicationContext et @ActivityContext.

Le code de Dagger et celui de Hilt peuvent coexister dans le même codebase. Toutefois, dans la plupart des cas, il est préférable d'utiliser Hilt pour gérer l'ensemble de votre utilisation de Dagger sur Android. Pour migrer un projet qui utilise Dagger vers Hilt, consultez le guide de migration et l'atelier de programmation sur la migration de Dagger vers Hilt.

Ressources supplémentaires

Pour en savoir plus sur Hilt, consultez les ressources supplémentaires suivantes.

Exemples

Ateliers de programmation

Blogs