Inyección de dependencias en Android (original) (raw)

La inyección de dependencias (DI) es una técnica muy utilizada en programación y adecuada para el desarrollo de Android. Si sigues los principios de la DI, sentarás las bases para una buena arquitectura de apps.

Implementar la inyección de dependencias te proporciona las siguientes ventajas:

Aspectos básicos de la inyección de dependencias

Antes de analizar específicamente la inyección de dependencias en Android, en esta página se proporciona una descripción más general de cómo funciona la inyección de dependencias.

¿Qué es la inyección de dependencias?

Las clases suelen requerir referencias a otras clases. Por ejemplo, una clase Car podría necesitar una referencia a una clase Engine. Estas clases se llaman dependencias y, en el ejemplo, la clase Car necesita una instancia de la clase Engine, de la que depende para ejecutarse.

Una clase puede obtener un objeto que necesita de tres maneras distintas:

  1. La clase construye la dependencia que necesita. En el ejemplo anterior, Car crea e inicializa su propia instancia de Engine.
  2. La toma de otro lugar. Algunas API de Android, como los métodos get de Context y getSystemService(), funcionan de esta manera.
  3. La recibe como parámetro. La app puede proporcionar estas dependencias cuando se construye la clase o pasarlas a las funciones que necesitan cada dependencia. En el ejemplo anterior, el constructor Car recibe Engine como parámetro.

La tercera opción es la inyección de dependencias. Con este enfoque, tomas las dependencias de una clase y las proporcionas en lugar de hacer que la instancia de la clase las obtenga por su cuenta.

Por ejemplo: Sin inyección de dependencias, la representación de un Car que crea su propia dependencia Engine se ve así en el código:

Kotlin

class Car {

private val engine = Engine()

fun start() {
    engine.start()
}

}

fun main(args: Array) { val car = Car() car.start() }

Java

class Car {

private Engine engine = new Engine();

public void start() {
    engine.start();
}

}

class MyApp { public static void main(String[] args) { Car car = new Car(); car.start(); } }

Clase Car sin inyección de dependencias

Este no es un ejemplo de inyección de dependencias, porque la clase Car está construyendo su propio Engine, lo que puede ser problemático debido a lo siguiente:

¿Cómo se ve el código con la inyección de dependencias? En lugar de que las diferentes instancias de Car construyan su propio objeto Engine durante la inicialización, cada una recibe un objeto Engine como parámetro en su constructor:

Kotlin

class Car(private val engine: Engine) { fun start() { engine.start() } }

fun main(args: Array) { val engine = Engine() val car = Car(engine) car.start() }

Java

class Car {

private final Engine engine;

public Car(Engine engine) {
    this.engine = engine;
}

public void start() {
    engine.start();
}

}

class MyApp { public static void main(String[] args) { Engine engine = new Engine(); Car car = new Car(engine); car.start(); } }

Clase Car con inyección de dependencias

La función main usa Car. Debido a que Car depende de Engine, la app crea una instancia de Engine y, luego, la usa para construir una instancia de Car. Los beneficios de este enfoque basado en DI son los siguientes:

Existen dos formas principales de realizar la inyección de dependencias en Android:

Kotlin

class Car { lateinit var engine: Engine

fun start() {
    engine.start()
}

}

fun main(args: Array) { val car = Car() car.engine = Engine() car.start() }

Java

class Car {

private Engine engine;

public void setEngine(Engine engine) {
    this.engine = engine;
}

public void start() {
    engine.start();
}

}

class MyApp { public static void main(String[] args) { Car car = new Car(); car.setEngine(new Engine()); car.start(); } }

Inyección de dependencias automatizada

En el ejemplo anterior, creaste, proporcionaste y administraste por tu cuenta las dependencias de las diferentes clases, sin recurrir a una biblioteca. Esto se denomina inyección de dependencias a mano o inyección de dependencias manual. En el ejemplo de Car, solo había una dependencia, pero, si hay varias dependencias y clases, la inyección manual puede resultar más tediosa. Además, la inyección de dependencias manual presenta varios problemas:

Hay bibliotecas que resuelven este problema automatizando el proceso de creación y provisión de dependencias. Se dividen en dos categorías:

Dagger es una biblioteca de inserción de dependencias popular para Java, Kotlin y Android que mantiene Google. Dagger facilita el uso de la DI en tu app mediante la creación y administración del grafo de dependencias. Proporciona dependencias totalmente estáticas y en tiempo de compilación que abordan muchos de los problemas de desarrollo y rendimiento de las soluciones basadas en reflexiones, como Guice.

Alternativas a la inserción de dependencias

Una alternativa a la inserción de dependencias es usar un localizador de servicios. El patrón de diseño del localizador de servicios también mejora el desacoplamiento de clases de las dependencias concretas. En este procedimiento, creas una clase conocida como localizador de servicios que, a su vez, crea y almacena dependencias, y luego proporciona esas dependencias a pedido.

Kotlin

object ServiceLocator { fun getEngine(): Engine = Engine() }

class Car { private val engine = ServiceLocator.getEngine()

fun start() {
    engine.start()
}

}

fun main(args: Array) { val car = Car() car.start() }

Java

class ServiceLocator {

private static ServiceLocator instance = null;

private ServiceLocator() {}

public static ServiceLocator getInstance() {
    if (instance == null) {
        synchronized(ServiceLocator.class) {
            instance = new ServiceLocator();
        }
    }
    return instance;
}

public Engine getEngine() {
    return new Engine();
}

}

class Car {

private Engine engine = ServiceLocator.getInstance().getEngine();

public void start() {
    engine.start();
}

}

class MyApp { public static void main(String[] args) { Car car = new Car(); car.start(); } }

El patrón del localizador de servicios se diferencia de la inyección de dependencias en la forma en que se consumen los elementos. Con el patrón del localizador de servicios, las clases tienen el control y solicitan que se inyecten objetos. Con la inyección de dependencias, la app tiene el control e inyecta los objetos solicitados de manera proactiva.

En comparación con la inyección de dependencias:

Cómo usar Hilt en tu app para Android

Hilt es la biblioteca de Jetpack recomendada para la inserción de dependencias en Android. Hilt establece 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 sobre Hilt, consulta Inserción de dependencias con Hilt.

Conclusión

La inyección de dependencias le proporciona a tu app las siguientes ventajas:

Para comprender por completo los beneficios de la inserción de dependencias, debes probarla de forma manual en tu app, como se muestra en la sección Inserción de dependencias manual.

Recursos adicionales

Para obtener más información sobre la inyección de dependencias, consulta los siguientes recursos adicionales.

Ejemplos