Inyección de valores en tiempo de ejecución con Dagger-Hilt
Desde que apareció Hilt para facilitar la inyección de dependencias en aplicaciones Android, no era posible la inyección de dependencias en tiempo de ejecución sin utilizar librerías ajenas a Dagger o Hilt. Desde la versión 2.31 se incorpora en Dagger la anotación @AssistedInject
. Con esta anotación vamos a ser capaces de indicar a Dagger-Hilt que dependencias se tienen que resolver en tiempo de ejecución y retrasar la inyección de esos parámetros hasta tener los valores.
Esto era necesario para poder inyectar valores en los constructores de los ViewModel y poder ejecutar alguna operación en el método init
del mismo. Como puede ser una petición a una API externa o bien una consulta en la base de datos local.
En este artículo veremos como implementar el @AssistedInject
de Dagger para la inyección de valores en tiempo de ejecución en ViewModels con Hilt.
Instalación
En el fichero build.gradle
raíz del proyecto, incluiremos el siguiente classpath:
|
|
Una vez añadido el classpath añadiremos el plugin de Hilt en el fichero build.gradle
del módulo app.
|
|
Y también las siguientes líneas a nuestras dependencias:
|
|
También hay que tener en cuenta tener añadido en nuestro build.gradle
el plugin de kapt
. Para ello añadiremos lo siguiente a nuestro archivo de build.gradle
del módulo app junto al resto de plugins:
|
|
Esa son las dependencias necesarias para implementar Hilt en nuestro proyecto. A lo largo de este post se usan distintas librerías como que no se definen en este artículo.
En este enlace puedes ver un ejemplo de un archivo build.gradle
completo: app/build.gradle
Implementación
Para este ejemplo usaremos una clase repositorio encargada de recibir el nombre de usuario y devolver un mensaje de bienvenida. Para ello crearemos la siguiente interfaz:
|
|
Y su implementación:
|
|
Anotamos el constructor con @Inject
para posteriormente poder declarar un @Binds
en el módulo de Hilt e inyectar la implementación cada vez que se pida una interfaz del tipo UserRepository.
Vamos a crear el siguiente ViewModel
que será el encargado de recibir el nombre del usuario desde el Activity
o Fragment
y llamar al repositorio para recibir el mensaje de bienvenida:
|
|
En este ViewModel
podemos ver como se anota el constructor con @AssistedInject
para indicar a Dagger-Hilt que esta clase contiene dependencias que se deben inyectar en tiempo de ejecución. Esas dependencias están anotadas con @Assisted
.
Para poder crear el ViewModel
con la extensión by viewModels()
de la librería de AndroidX debemos crear la Factory
que más tarde pasaremos a la extensión:
|
|
Como puedes ver necesitamos la interfaz UserViewModelAssistedFactory que es la encargada de proveer los parámetros en tiempo de ejecución. Esta interfaz la implementamos de la siguiente forma:
|
|
Se trata de una interfaz con una función create
que recibe los parámetros a inyectar en tiempo de ejecución. En nuestro caso solo necesitamos el name
, pero en caso de necesitar inyectar más parámetros en tiempo de ejecución, se pasarían como parámetro a esta función.
Con esto ya podemos completar nuestro ViewModel con la lógica necesaria para pedir la respuesta al repositorio y exponer al Fragment
o Activity
a través de un StateFlow.
El ViewModel completo quedaría:
|
|
Relativo a Hilt
solo nos faltaría declarar el módulo indicando como proveer las dependencias. Para este ejemplo usaremos el siguiente módulo:
|
|
En este módulo declaramos un Dispatcher
para que sea más sencillo testear este ViewModel en un futuro. Y hacemos @Binds
de nuestra interfaz UserRepository
con su implementación UserRepositoryImpl
.
Ahora podemos inyectar nuestro repositorio en una Activity
o Fragment
de la siguiente forma:
|
|
Simplemente nos faltaría observar los cambios en el StateFlow del ViewModel para poder actualizar nuestra UI. Eso se haría de la siguiente manera en un Fragment
aunque sería muy similar en un Activity
|
|
Recordar que es necesario anotar una clase que extienda de Application con
@HiltAndroidApp
y cada uno de las Activities o Fragments que usen inyección con Hilt con la anotación@AndroidEntryPoint
.
Conclusión
Como hemos podido observar con @AssistedInject
de Dagger podemos inyectar valores en tiempo de ejecución de una forma sencilla y podemos seguir utilizando los navArgs
de AndroidX.
En el siguiente repositorio teneis el ejemplo completo: HiltAssistedInject