Inyección de Dependencias en Android (skel) 💉
Tags: android, kotlin, mobile
Published: 2023-06-01 07:56:12

La principal idea de dicho post es tener un esqueleto básico de la Inyección de Dependencias en Android mediante Dagger Hilt. 

Por lo tanto, obviaré explicaciones sobre dicho patrón y que problemas resuelve. 

Adjunto un enlace de una publicación de `freeCodeCamp` en la que se explica: [Dependency Injection: what it is](https://www.freecodecamp.org/news/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f/)

- [Dependencias Dagger Hilt](#dependencias-dagger-hilt)
- [Clase Application](#clase-application)
- [Proporcionar las dependencias](#proporcionar-las-dependencias)
- [Inyección en constructor](#inyección-en-constructor)
- [Inyección en atributos](#inyección-en-atributos)
- [Módulos](#módulos)
    - [Proveer dependencias en módulos](#proveer-dependencias-en-módulos)

- [Testing de la Inyección de Dependencias](#testing-de-la-inyección-de-dependencias)
    - [Dependencias Testing](#dependencias-testing)
    - [testInstrumentationRunner](#testinstrumentationrunner)
    - [CustomRunner](#customrunner)
    - [Clase de Test](#clase-de-test)

----


# Dependencias Dagger Hilt
[Documentación Oficial](https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419#setup)

**`project/build.gradle`**
```groovy
plugins {  
	...
    id 'com.google.dagger.hilt.android' version '2.44' apply false  
}
```


**`project/app/build.gradle`**
```groovy
...  
plugins {  
    id 'kotlin-kapt'  
    id 'com.google.dagger.hilt.android'  
}  
  
dependencies {  
    implementation "com.google.dagger:hilt-android:2.44"  
    kapt "com.google.dagger:hilt-compiler:2.44"  
}  
  
kapt {  
    correctErrorTypes true  
}
```

----

# Clase Application
[Documentación Oficial](https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419#application-class)

> Obligatoria

```kotlin
@HiltAndroidApp  
class BaseApp: Application() { }
```

> Propiedad nombre (`AndroidManifest.xml`)
```xml
<?xml version="1.0" encoding="utf-8"?>  
<manifest ... >
    <application
        android:name=".BaseApp" />
  
</manifest>
```

---

# Proporcionar las dependencias
[Documentación Oficial](https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419#android-classes)

```kotlin
@HiltViewModel
class MyCustomViewModel: ViewModel() { ... }
```

```kotlin
@AndroidEntryPoint
class MyActivity: ComponentActivity() { ... }
```

![Android Entry point](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6al0sybp44ocq4n763px.png)

----

# Inyección en constructor
### *(mejor)*

[Documentación Oficial](https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419#define-bindings)

```kotlin
@HiltViewModel
class MyCustomViewModel @Inject
constructor(private var myRepo: MyRepository) : ViewModel()
```

# Inyección en atributos
### *(peor)*
```kotlin
@HiltViewModel
class MyCustomViewModel: ViewModel() {

	@Inject
	lateinit var myRepo: MyRepository

}
```

----

# Módulos
[Documentación Oficial](https://developer.android.com/training/dependency-injection/hilt-android?hl=es-419#hilt-modules)
```kotlin
@Module  
@InstallIn(SingletonComponent::class)  

class AppModule {  
	// Funciones para proveer clases
}
```

*Opciones `@InstallIn`*

![Hilt components with injector](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ho4c10hosls7zv4orxa0.png)


## Proveer dependencias en módulos
```kotlin
@Singleton  
@Provides  
fun providesDatabase
	(@ApplicationContext context: Context): UsersDb {  
		return Room
			.databaseBuilder(
				context, 
				UsersDb::class.java, "users_db")  
        .build()  
}

@Provides  
fun provideApplicationContext(@ApplicationContext context: Context): Context {  
    return context  
}
```

---

# Testing de la Inyección de Dependencias 
### *(androidTests/)*

1. Importar dependencias
2. Cambiar el `testInstrumentationRunner` en `app/build.gradle`
3. Crear un `CustomRunner` de testing utilizando la clase `HiltTestApplication`
4. Anotar la clase de test con `@HiltAndroidTest`

## Dependencias Testing
> `app/build.gradle`
```groovy
dependencies {

	testImplementation 'com.google.dagger:hilt-android-testing:2.44'  
	kaptTest 'com.google.dagger:hilt-android-compiler:2.44'  
	androidTestImplementation 'com.google.dagger:hilt-android-testing:2.44'  
	kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.44'
}
```

## testInstrumentationRunner
> `app/build.gradle`
```groovy
android {

	testInstrumentationRunner "com.package.name.CustomRunner"
}
```

## CustomRunner
```kotlin
class CustomRunner : AndroidJUnitRunner() {  
    override fun newApplication(  
        cl: ClassLoader?,  
        className: String?,  
        context: Context?  
    ): Application {  
        return super.newApplication(cl, HiltTestApplication::class.java.name, context)  
    }  
}

```

## Clase de Test

```kotlin
@HiltAndroidTest
@RunWith(AndroidJUnit4::class)  
class DITests {  
	// Es la clase que inyecta dependencias en los tests
    @get:Rule  
    var hiltRule = HiltAndroidRule(this)  

	// Inyección (pero no se inyecta)
    @Inject  
    lateinit var context: Context  

	// Aqui es donde se inyecta
    @Before  
    fun setUp() {  
        hiltRule.inject()  
    }  
  
    @Test  
    fun testContextInjection() {  
        val cacheDir = context.cacheDir.name  
        assertEquals(cacheDir, "cache")  
    }  
}
```