Cómo usar Dagger 2 en Android con Kotlin (KDA 20)
Antonio Leiva

Prácticamente todo el mundo que quiere crear código en Android de forma desacoplada y fácil de testear, recurre tarde o temprano a Dagger para ello.

Aunque hay alguna cosa que funciona un poco diferente a la hora de configurar Dagger en Kotlin, la mayor parte es bastante sencilla, y en pocos pasos te la voy a mostrar hoy aquí.

También decirte que, gracias a la potencia de Kotlin, hay otras maneras de solventar la inyección, e incluso algunas librerías hechas exclusivamente en Kotlin para ello.

Pero Dagger sigue siendo una opción perfectamente válida, y una de las más versátiles (si no la que más).

Si quieres aprender cuál es la situación de Kotlin en el mercado, por qué debes aprenderlo y los primeros pasos para que veas lo fácil que es, he preparado un training gratuito de hora y media al que puedes unirte haciendo click aquí

Disclaimer: En este artículo no voy a explicar cómo se usa Dagger 2, esto ya se da por sabido. Si tienes alguna duda, busca alguno de los muchos artículos que puedes encontrar por Internet.

Configurando el proyecto para que utilice Dagger 2

Si ya tienes configurado el plugin de Kotlin en tu proyecto, lo único que necesitas es configurar kapt.

Si ya utilizabas Dagger, seguramente conoces apt. kapt no es más que la versión para Kotlin, que crea las clases autogeneradas necesarias para Dagger.

Para configurarlo, necesitas añadir lo siguiente al build.gradle:

kapt {
    generateStubs = true
}

Puedes añadirlo justo antes de la sección de dependencies. En algún sitio leí (no recuerdo dónde), que en un tiempo esta parte tampoco será necesaria.

Ahora ya solo necesitas añadir las dependencias del compilador de Dagger (usando kapt para que no se incluya en el apk) y de la librería propiamente dicha:

kapt 'com.google.dagger:dagger-compiler:2.5'
compile 'com.google.dagger:dagger:2.5'

Ya está todo listo para empezar a utilizar Dagger.

Implementación del módulo principal

Como bien sabes, para el grafo principal vas a necesitar un Module y un Component.

El módulo de aplicación, en este caso sencillo, sólo devolverá la instancia de la aplicación propiamente dicha.

Para ello crearemos un clase anotada con @Module, que recibirá por constructor la instancia de aplicación, la almacenará en una property, y la devolvería mediante un método anotado con @Provides @Singleton:

@Module class AppModule(val app: App) {
    @Provides @Singleton fun provideApp() = app
}

Ya ves que, incluso para esta clase tan sencilla, el código es bastante más simple que en Java.

Ahora nos queda implementar el Component, que necesita un array de módulos a cargar, y especificar quién lo va a poder inyectar manualmente:

@Singleton
@Component(modules = arrayOf(AppModule::class))
interface AppComponent {
    fun inject(app: App)
}

Ahora ya solo queda crear la clase App, que se encargará de generar el grafo:

class App : Application() {

    val component: AppComponent by lazy {
        DaggerAppComponent
                .builder()
                .appModule(AppModule(this))
                .build()
    }

    override fun onCreate() {
        super.onCreate()
        component.inject(this)
    }
}

Lo interesante a ver aquí es que, gracias a la declaración lazy, podemos especificar el valor del grafo en la definición de la propiedad, y además conseguir así un acceso de solo lectura a dicha propiedad.

el código definido por la propiedad no se ejecutará hasta que no se haga component.inject(this), de tal forma que para ese momento this ya existe y se puede crear el grafo de forma segura.

Implementación de un módulo por ámbito

Los módulos por ámbito permiten que esa parte del grafo solo viva durante el tiempo de vida del objeto que lo crea.

De esta forma, podemos crear subgrafos que vivan y mueran con una Activity, por ejemplo.

Nos crearíamos nuestro módulo con lo que necesitemos:

@Module
class HomeModule(val activity: HomeActivity) {
}

Un Subcomponent de forma muy parecida al anterior, indicando que se va a inyectar en la HomeActivity:

@Singleton
@Subcomponent(modules = arrayOf(HomeModule::class))
interface HomeComponent {
    fun inject(activity: HomeActivity)
}

Y un método plus en AppComponent, para indicar que a ese componente se le pueden añadir subcomponentes de ese tipo:

interface AppComponent {
    ...
    fun plus(homeModule: HomeModule): HomeComponent
}

Ahora, en la HomeActivity ya solo necesitas declarar el subcomponente:

val component by lazy { app.component.plus(HomeModule(this)) }

Y ya lo puedes inyectar tras el setContentView:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    component.inject(this)
}

Si te preguntas de dónde viene app , se trata de una propiedad de extensión escrita tal que así:

val Activity.app: App
    get() = application as App

Es simplemente una forma de evitar tener que hacer casting cada vez que accedes a application si tienes una clase propia.

Conclusión

Así de sencillo es utilizar Dagger 2 en Kotlin. Ya no tienes excusa para no hacer tu código más desacoplado también en Kotlin.

Si todo esto te apasiona tanto como a mí, te animo a que te apuntes a mi training gratuito donde te contaré todo lo que necesitas para aprender a crear tus Apps Android en Kotlin desde cero.

Quizá también te interese…

Kotlin 1.5.0 : Las 5 novedades que puedes empezar a usar hoy

Kotlin 1.5.0 : Las 5 novedades que puedes empezar a usar hoy

Kotlin 1.5.0 ya está aquí, y como siempre trae una serie de novedades que te van a interesar muchísimo. Cabe destacar que a partir de ahora, de acuerdo las nuevas versiones de Kotlin se lanzarán cada 6 meses, independientemente de las nuevas funcionalidades que...

¿Qué es Kotlin y para qué sirve?

¿Qué es Kotlin y para qué sirve?

Kotlin es un lenguaje de programación de código abierto creado por JetBrains que se ha popularizado gracias a que se puede utilizar para programar aplicaciones Android. Pero si has llegado hasta aquí pensando que Kotlin solo se puede usar en Android, lo que te voy a...

2 formas de recolectar Flows en la UI que SÍ funcionan

2 formas de recolectar Flows en la UI que SÍ funcionan

En la serie de artículos sobre Programación Reactiva con Flow hemos visto muchos conceptos, y hemos aprendido cómo aplicarlos al desarrollo Android. Pero hay algo que no hemos hecho del todo bien. Esto es la recolección de Flows desde la Activity (o el Fragment, en...

3 Comentarios

  1. David

    ¿De dónde sale la referencia app en esta línea de código?
    val component by lazy { app.component.plus(HomeModule(this)) }

    Supongo que será de App, pero no la encuentra. ¿Hay que hacer añadir algún código adicional para obtener una referencia a ‘app’?

    Responder
    • Antonio Leiva

      Es una propiedad de extensión para hacer casting de Application. He añadido el código al ejemplo. Gracias!

      Responder
  2. borja gonzalez

    Hay alguna manera de utilizar multibindings con Dagger2 y Kotlin?

    Tengo este provides pero dagger no me lo detecta. En cambio en java no tengo problema.

    @Singleton
    @Provides
    @IntoSet
    fun providesQueries(getTokenByCredentials: GetTokenByCredentialsApi): Query{
    return getTokenByCredentials
    }

    Responder

Enviar un comentario

Los datos personales que proporciones a través de este formulario quedarán registrados en un fichero de Antonio Leiva Gordillo, con el fin de gestionar los comentarios que realizas en este blog. La legitimación se realiza a través del consentimiento de la parte interesada. Si no se acepta, no podrás comentar en este blog. Los datos que proporciona solo se utilizan para evitar el correo no deseado y no se usarán para nada más. Puede ejercer los derechos de acceso, rectificación, cancelación y oposición en contacto@devexperto.com.

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Acepto la política de privacidad *