Inserción de dependencias con Hilt

Hilt es una biblioteca de inserción de dependencias para Android que permite reducir el trabajo repetitivo de insertar dependencias de forma manual en tu proyecto. Para la inserción manual de dependencias, debes construir cada clase y sus dependencias de forma manual, y usar contenedores para reutilizar y administrar las dependencias.

Hilt proporciona una forma estándar de usar la inserción de dependencias en tu aplicación, ya que proporciona contenedores para cada clase de Android en tu proyecto y administra automáticamente sus ciclos de vida. Hilt se basa en la popular biblioteca de inserción de dependencias Dagger y se beneficia de la corrección en tiempo de compilación, el rendimiento del entorno de ejecución, la escalabilidad y la compatibilidad con Android Studio que proporciona. Para obtener más información, consulta Hilt y Dagger.

En esta guía, se explican los conceptos básicos de Hilt y los contenedores que este genera. También se incluye una demostración de cómo realizar un arranque de una app existente para usar Hilt.

Cómo agregar dependencias

Primero, agrega el complemento hilt-android-gradle-plugin al archivo build.gradle raíz de tu proyecto:

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
}

Luego, aplica el complemento de Gradle y agrega estas dependencias a tu archivo 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 {
  id("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 usa funciones de Java 8. Para habilitar Java 8 en tu proyecto, agrega lo siguiente al archivo 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
  }
}

Clase de aplicación de Hilt

Todas las apps que usan Hilt deben contener una clase Application anotada con @HiltAndroidApp.

@HiltAndroidApp activa la generación de código de Hilt, incluida una clase base para tu aplicación que sirve como contenedor de dependencia a nivel de la aplicación.

Kotlin

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

Java

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

Se adjunta este componente generado por Hilt al ciclo de vida del objeto Application y le proporciona dependencias. Además, es el componente superior de la app, lo que significa que otros componentes pueden acceder a las dependencias que proporciona.

Cómo inyectar dependencias en clases de Android

Una vez que se configura Hilt en tu clase Application y hay un componente disponible en el nivel de la aplicación, Hilt puede proporcionar dependencias para otras clases de Android que tengan la anotación @AndroidEntryPoint:

Kotlin

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

Java

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

En la actualidad, la versión de Hilt admite las siguientes clases de Android:

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

Si anotas una clase de Android con @AndroidEntryPoint, también debes anotar las clases de Android que dependen de ella. Por ejemplo, si anotas un fragmento, también debes anotar todas las actividades en las que uses ese fragmento.

@AndroidEntryPoint genera un componente individual de Hilt para cada clase de Android de tu proyecto. Estos componentes pueden recibir dependencias de sus respectivas clases superiores, como se describe en Jerarquía de los componentes.

Para obtener dependencias de un componente, usa la anotación @Inject a fin de realizar la inyección de campo:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Las clases que se pueden insertar con Hilt pueden tener otras clases base que también usen la inserción. Esas clases no necesitan la anotación @AndroidEntryPoint si son abstractas.

Para obtener más información acerca de en qué devolución de llamada de ciclo de vida se inserta una clase de Android, consulta Ciclos de vida de los componentes.

Cómo definir las vinculaciones de Hilt

Para realizar la inyección de campo, Hilt debe saber cómo proporcionar instancias de las dependencias necesarias del componente correspondiente. Una vinculación contiene la información necesaria para proporcionar instancias de un tipo como dependencia.

Una forma de proporcionar información de vinculación a Hilt es la inyección de constructor. Usa la anotación @Inject en el constructor de una clase para indicarle a Hilt cómo proporcionar instancias de esa clase:

Kotlin

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

Java

public class AnalyticsAdapter {

  private final AnalyticsService service;

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

Los parámetros de un constructor anotado de una clase son las dependencias de esa clase. En el ejemplo, AnalyticsAdapter tiene AnalyticsService como dependencia. Por lo tanto, también debe proporcionar instancias de AnalyticsService.

Módulos de Hilt

A veces, el tipo no se puede insertar con un constructor, lo que puede suceder por varios motivos. Por ejemplo, no puedes insertar una interfaz con un constructor. Tampoco puedes inyectar con un constructor un tipo que no sea de tu propiedad, como una clase de una biblioteca externa. En estos casos, puedes proporcionar información de vinculación mediante módulos de Hilt.

Un módulo de Hilt es una clase anotada con @Module. Al igual que los módulos de Dagger, informa a Hilt cómo proporcionar instancias de determinados tipos. A diferencia de los módulos de Dagger, debes anotar los módulos de Hilt con @InstallIn para indicarle a Hilt en qué clase de Android se usará o instalará cada módulo.

Las dependencias que proporcionas en los módulos de Hilt están disponibles en todos los componentes generados que están asociados con la clase de Android en la que instalas el módulo de Hilt.

Cómo inyectar instancias de interfaces con @Binds

Considera el ejemplo de AnalyticsService. Si AnalyticsService es una interfaz, no puedes inyectarla con un constructor. En cambio, debes proporcionarle a Hilt la información de vinculación creando una función abstracta anotada con @Binds dentro de un módulo de Hilt.

La anotación @Binds le indica a Hilt qué implementación debe usar cuando necesite proporcionar una instancia de una interfaz.

La función anotada le proporciona a Hilt la siguiente información:

  • El tipo de datos que muestra la función le indica a Hilt las instancias de qué interfaz proporciona la función.
  • El parámetro de la función le indica a Hilt qué implementación debe proporcionar.

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

El módulo AnalyticsModule de Hilt está anotado con @InstallIn(ActivityComponent.class) porque deseas que Hilt inyecte esa dependencia en ExampleActivity. Esta anotación indica que todas las dependencias de AnalyticsModule están disponibles en todas las actividades de la app.

Cómo insertar instancias con @Provides

Las interfaces no son el único caso en el que no puedes insertar un tipo con un constructor. La inserción de constructor tampoco es posible si la clase no es de tu propiedad porque proviene de una biblioteca externa (clases como Retrofit, OkHttpClient o bases de datos de Room), o si las instancias deben crearse con el patrón de compilador.

Considera el ejemplo anterior. Si la clase AnalyticsService no es de tu propiedad, puedes indicarle a Hilt cómo proporcionar instancias de este tipo creando una función dentro de un módulo de Hilt y anotando esa función con @Provides.

La función anotada le proporciona a Hilt la siguiente información:

  • El tipo de datos que se muestra le indica a Hilt las instancias de qué tipo proporciona la función.
  • Los parámetros de la función le indican a Hilt cuáles son las dependencias del tipo correspondiente.
  • El cuerpo de la función le indica a Hilt cómo proporcionar una instancia del tipo correspondiente. Hilt ejecuta el cuerpo de la función cada vez que necesita proporcionar una instancia de ese tipo.

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

Cómo proporcionar varias vinculaciones para el mismo tipo

En los casos en los que necesites que Hilt proporcione diferentes implementaciones del mismo tipo como dependencias, debes proporcionarle varias vinculaciones. Puedes definir varias vinculaciones para el mismo tipo con calificadores.

Un calificador es una anotación que se usa para identificar una vinculación específica de un tipo cuando ese tipo tiene varias vinculaciones definidas.

Considera el ejemplo. Si necesitas interceptar llamadas a AnalyticsService, puedes usar un objeto OkHttpClient con un interceptor. En otros servicios, es posible que debas interceptar llamadas de otra manera. En ese caso, debes indicarle a Hilt cómo proporcionar dos implementaciones diferentes de OkHttpClient.

Primero, define los calificadores que usarás para anotar los métodos @Binds o @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 {}

Luego, Hilt necesita saber cómo proporcionar una instancia del tipo correspondiente a cada calificador. En este caso, puedes usar un módulo de Hilt con @Provides. Ambos métodos muestran el mismo tipo de datos, pero los calificadores los etiquetan como dos vinculaciones diferentes:

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

Puedes inyectar el tipo específico que necesitas anotando el campo o parámetro con el calificador correspondiente:

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;
  ...
}

Según la práctica recomendada, si agregas un calificador a un tipo, debes agregar calificadores a todas las formas posibles en que se proporciona esa dependencia. Si sales de la implementación base o común sin un calificador, se pueden producir errores y Hilt podría insertar una dependencia incorrecta.

Calificadores predefinidos en Hilt

Hilt proporciona algunos calificadores predefinidos. Por ejemplo, debido a que podrías necesitar la clase Context de la aplicación o de la actividad, Hilt proporciona los calificadores @ApplicationContext y @ActivityContext.

Supongamos que la clase AnalyticsAdapter del ejemplo necesita el contexto de la actividad. En el siguiente código, se muestra cómo proporcionar el contexto de la actividad a 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;
  }
}

Para obtener información sobre otras vinculaciones predefinidas disponibles en Hilt, consulta Vinculaciones predeterminadas de los componentes.

Componentes generados para clases de Android

Para cada clase de Android en la que puedes realizar la inserción de campo, hay un componente de Hilt asociado al que puedes hacer referencia en la anotación @InstallIn. Cada componente de Hilt es responsable de inyectar sus vinculaciones en la clase de Android correspondiente.

En los ejemplos anteriores, se demostró el uso de ActivityComponent en los módulos de Hilt.

Hilt proporciona los siguientes componentes:

Componente de Hilt Inyector para
SingletonComponent Application
ActivityRetainedComponent N/A
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View anotada con @WithFragmentBindings
ServiceComponent Service

Ciclos de vida de los componentes

Hilt crea y destruye automáticamente instancias de clases generadas por componentes siguiendo el ciclo de vida de las clases de Android correspondientes.

Componente generado Creado en Destruido en
SingletonComponent Application#onCreate() Se destruyó Application
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent Se creó ViewModel Se destruyó ViewModel
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() Se destruyó View
ViewWithFragmentComponent View#super() Se destruyó View
ServiceComponent Service#onCreate() Service#onDestroy()

Alcances de los componentes

De forma predeterminada, en Hilt, las vinculaciones no tienen alcance. Esto significa que, cada vez que tu app solicita la vinculación, Hilt crea una instancia nueva del tipo necesario.

En el ejemplo, cada vez que Hilt proporciona AnalyticsAdapter como dependencia a otro tipo o mediante la inserción de campo (como en ExampleActivity), también proporciona una nueva instancia de AnalyticsAdapter.

Sin embargo, también permite que una vinculación tenga un alcance para un componente en particular. Hilt solo crea una vinculación con alcance una vez por instancia del componente al que se aplica la vinculación y todas las solicitudes para esa vinculación comparten la misma instancia.

En la siguiente tabla, se enumeran las anotaciones de alcance para cada componente generado:

Clase de Android Componente generado Alcance
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View anotada con @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

En el ejemplo, si defines el alcance de AnalyticsAdapter mediante ActivityComponent con @ActivityScoped, Hilt proporciona la misma instancia de AnalyticsAdapter durante el ciclo de vida de la actividad correspondiente:

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;
  }
  ...
}

Supongamos que AnalyticsService tiene un estado interno que requiere que se use siempre la misma instancia (no solo en ExampleActivity, sino en cualquier parte de la app). En este caso, es apropiado establecer el alcance de AnalyticsService en el SingletonComponent. Como resultado, cada vez que el componente necesita proporcionar una instancia de AnalyticsService, proporciona siempre la misma instancia.

En el siguiente ejemplo, se muestra cómo definir el alcance de una vinculación a un componente en un módulo de Hilt. El alcance de una vinculación debe coincidir con el alcance del componente en el que está instalada, de modo que en este ejemplo debes instalar AnalyticsService en SingletonComponent, en lugar de 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);
  }
}

Para obtener más información sobre los alcances de los componentes de Hilt, consulta Alcance en Android y Hilt.

Jerarquía de los componentes

Instalar un módulo en un componente permite acceder a sus vinculaciones como una dependencia de otras vinculaciones, en ese componente o en cualquier componente secundario de la jerarquía de componentes:

ViewWithFragmentComponent se encuentra en FragmentComponent. FragmentComponent y ViewComponent se encuentran en ActivityComponent. ActivityComponent se encuentra en ActivityRetainedComponent. ViewModelComponent se encuentra en ActivityRetainedComponent. ActivityRetainedComponent y ServiceComponent se encuentran bajo SingletonComponent.
Figura 1: Jerarquía de los componentes que genera Hilt

Vinculaciones predeterminadas de los componentes

Cada componente de Hilt incluye un conjunto de vinculaciones predeterminadas que puede insertar como dependencias en tus propias vinculaciones personalizadas. Ten en cuenta que estas vinculaciones corresponden a la actividad y a los tipos de fragmentos generales, no a una subclase específica. Esto se debe a que Hilt usa una sola definición de componente de actividad para inyectar todas las actividades. Cada actividad tiene una instancia diferente de este componente.

Componente de Android Vinculaciones predeterminadas
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 vinculación de contexto de la aplicación también está disponible si se usa @ApplicationContext. Por ejemplo:

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 vinculación de contexto de actividad también está disponible si se usa @ActivityContext. Por ejemplo:

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

Cómo inyectar dependencias en clases no compatibles con Hilt

Hilt incluye compatibilidad con las clases de Android más comunes. Sin embargo, es posible que debas realizar una inyección de campo en las clases que Hilt no admite.

En esos casos, puedes crear un punto de entrada mediante la anotación @EntryPoint. Un punto de entrada es el límite entre el código que está administrado por Hilt y el código que no lo está. Es el punto en el que el código ingresa por primera vez en el grafo de objetos que Hilt administra. Los puntos de entrada le permiten a Hilt usar código que no administra a fin de proporcionar dependencias dentro del grafo de dependencias.

Por ejemplo, Hilt no admite directamente a los proveedores de contenido. Si quieres que un proveedor de contenido use Hilt para obtener algunas dependencias, debes definir una interfaz anotada con @EntryPoint para cada tipo de vinculación que deseas e incluir calificadores. Luego, agrega @InstallIn para especificar el componente en el que instalarás el punto de entrada, de la siguiente manera:

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

Para acceder a un punto de entrada, usa el método estático apropiado de EntryPointAccessors. El parámetro debería ser la instancia del componente o el objeto @AndroidEntryPoint que funciona como contenedor del componente. Asegúrate de que el componente que pasas como parámetro y el método estático EntryPointAccessors coincidan con la clase de Android en la anotación @InstallIn de la interfaz @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();
  }
}

En este ejemplo, debes usar el ApplicationContext para recuperar el punto de entrada porque este está instalado en SingletonComponent. Si la vinculación que quieres recuperar está en el ActivityComponent, deberías usar el ActivityContext.

Hilt y Dagger

Hilt se basa en la biblioteca de inserción de dependencias de Dagger, lo que proporciona una forma estándar de incorporar Dagger en una aplicación para Android.

Con respecto a Dagger, los objetivos de Hilt son los siguientes:

  • Simplificar la infraestructura relacionada con Dagger para las apps de Android
  • Crear un conjunto estándar de componentes y alcances para facilitar la configuración, la legibilidad y el uso compartido de código entre apps.
  • Proporcionar una manera fácil de aprovisionar diferentes vinculaciones para varios tipos de compilación, como pruebas, depuración o lanzamiento

Debido a que el sistema operativo Android crea una instancia de muchas de sus propias clases de frameworks, si usas Dagger en una app para Android, deberás escribir una cantidad considerable de código estándar. Hilt permite reducir la cantidad de código estándar que implica el uso de Dagger en una aplicación para Android. Hilt genera y proporciona automáticamente lo siguiente:

  • Componentes para integrar clases de frameworks de Android con Dagger que, de lo contrario, deberías crear a mano.
  • Anotaciones de alcance para usar con los componentes que Hilt genera automáticamente.
  • Vinculaciones predefinidas para representar clases de Android, como Application o Activity.
  • Calificadores predefinidos para representar a @ApplicationContext y @ActivityContext.

El código de Hilt y el de Dagger pueden coexistir en la misma base de código. Sin embargo, en la mayoría de los casos, lo mejor es usar Hilt para administrar todo el uso de Dagger en Android. Si deseas migrar un proyecto que usa Dagger a Hilt, consulta la guía de migración y el codelab Cómo migrar tu app de Dagger a Hilt.

Recursos adicionales

Para obtener más información sobre Hilt, consulta los siguientes recursos adicionales.

Ejemplos

Codelabs

Blogs