Inyectar Dependencias Con Hilt

En este tutorial verás una introducción para inyectar dependencias con Hilt en Android. Cada paso se ilustrará con un ejemplo simple que podrás descargar al final.

La Librería Hilt

Hilt es una librería de inyección de dependencias que hace parte del Android Jetpack.

Su objetivo es ayudarte a reducir el código repetitivo a la hora de implementar inyección de dependencias manual en tus proyectos Android.

A continuación verás los pasos para inyectar dependencias con Hilt.

1. Añadir Dependencias De Hilt

Usar Hilt en tu app Android requiere enlazar el siguiente plugin en tu archivo build.gradle a nivel de proyecto:

buildscript {
    /*...*/
    ext.hilt_version = "2.28-alpha"

    dependencies {
        /*...*/
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
        /*...*/
    }
}

Luego aplica los siguientes plugins y dependencias de gradle en tu archivo de módulo:

plugins {
    /*...*/
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}

android {
    /*...*/
}

dependencies {

    /*...*/
    // Hilt
    implementation "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-android-compiler:$hilt_version"

    /*...*/
}

Completa la configuración, usando el JDK 8 de Java en app/build.gradle, debido a que Hilt usa características de allí:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

2. Crear Clase Application Para Hilt

Hilt demanda que añadas una clase que herede de Application.

Recuerda que Application es el lugar en común, al que la mayoría de los componentes de Android pueden acceder. Esto hace que su clase derivada sea un excelente contenedor de dependencias. Ligando su esperanza de vida al de la app Android.

Al crear la subclase, anótala con @HiltAndroidApp para marcarla como el componente contenedor de dependencias.

@HiltAndroidApp
class CardsApplication : Application() {
    /*...*/
}

No olvides hacer referencia desde el AndroidManifest en el atributo android:name de la etiqueta <application>.

 <application
        android:name=".CardsApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.IntroducciónAHilt">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

3. Inyectar Dependencias Con Hilt

Para hacer que tus subclases del framework de Android hagan uso de Hilt, márcalas con @AndroidEntryPoint. Esto creará un contenedor de dependencias que es consciente del ciclo de vida de la clase.

Luego materializa el consumo de la dependencia en un campo, usando la anotación @Inject.

Por ejemplo: En nuestro ejemplo de cartas deseamos inyectar la fuente local directamente en el fragmento para cargas la lista de datos:

@AndroidEntryPoint
class CardsFragment : Fragment() {

    @Inject
    lateinit var cardsLocalStore: CardsLocalStore

    /*...*/
}

La solución consiste en usar una propiedad miembro de inicio tardío (cardsLocalStore) marcada con @Inject.

Puntos De Entrada Dependientes

Si anotas una clase de la cual dependa otra, como los fragmentos con las actividades, entonces debes anotarla también.

Eso es lo que pasa con CardsActivity en nuestro ejemplo, si no la anotas tendrás el siguiente error:

Hilt Fragments must be attached to an @AndroidEntryPoint Activity. Found: class com.develou.hilt1.cards.CardsActivity

Por lo que será indispensable que recuerdes anotar a tus actividades:

@AndroidEntryPoint
class CardsActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_cards)

        supportFragmentManager.commit {
            replace(R.id.container, CardsFragment())
            addToBackStack("Main")
        }
    }
}

4. Enlazar Constructores A La Inyección

Obviamente marcar el campo no es suficiente para que se genere el código necesario de inyección de la dependencia.

Por eso debes ir al constructor de la clase y anotarlo con @Inject para decirle a Hilt como proveer la instancia del tipo que deseas inyectar.

En el caso de la fuente local, completaremos la relación anotándola así:

class CardsLocalStore @Inject constructor() {
    fun getLocalCards() = listOf("Carta 1", "Carta 2", "Carta 3")
}

Esto hará que Android Studio muestre un ícono que informa la provisión/binding de la dependencia «CardsLocalStore() provides for CardsFragment».

Binding de dependencias en Hilt

Y que también marque el consumo desde el fragmento:

Consumo de dependencia en Hilt

Si presionas ambos iconos podrás navegar entre el binding y el consumo.

Ejemplo Con Hilt

El ejemplo discutido resuelve un caso de uso de lista, que muestra la colección de cartas de un juego para el usuario. Por cuestiones de simplicidad la interfaz es esta:

Puedes descargar el ejemplo visto en este tutorial de inyección de dependencias con Hilt desde el siguiente link.

A lo largo de los siguientes tutoriales este ejemplo irá creciendo para mostrar otros casos de inyección.

¿Qué Sigue?

Ahora puedes ir a ver el uso de módulos en Hilt (por escribir), los cuáles te permitirá proveer dependencias de clases a las cuáles no tienes acceso de construcción o para la creación de ejemplares de interfaces.

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!