Exemplos de Código: Capítulo 12 — Injeção de Dependências no Android

Especialização em Mobile Nativo com Android • Demonstrações Práticas Compiláveis


💻 Código de Demonstração: Arquitetura com DI Limpa usando Koin e Padrões Hilt

O código abaixo demonstra a orquestração e injeção de dependências corporativa no Android:

  1. Desacoplamento estrito entre contratos de interface (IRepository) e implementações concretas.
  2. Definição de escopos: Singletons (uma única instância na aplicação), Factory (novas instâncias) e ViewModel.
  3. Configuração idiomática com Koin DSL (module { singleOf(); viewModelOf() }) e anotações comparativas do Hilt / Dagger.
package br.com.portal.mobile.android.cap12

// =====================================================================
// 1. Contratos de Domínio e Implementações
// =====================================================================

interface IClienteRepository {
    fun buscarNomeCliente(id: String): String
}

class ClienteRepositoryRemoto : IClienteRepository {
    override fun buscarNomeCliente(id: String): String {
        return "Empresa Alfa Tecnologia (ID: $id)"
    }
}

interface IServicoAuditoria {
    fun registrarEvento(evento: String)
}

class ServicoAuditoriaConsole : IServicoAuditoria {
    override fun registrarEvento(evento: String) {
        println("[AUDITORIA DI] $evento")
    }
}

// =====================================================================
// 2. ViewModel com Injeção por Construtor (Constructor Injection)
// =====================================================================

class ClienteDetalhesViewModel(
    private val repository: IClienteRepository,
    private val auditoria: IServicoAuditoria
) {
    fun carregarDados(id: String): String {
        auditoria.registrarEvento("Consultando cliente: $id")
        return repository.buscarNomeCliente(id)
    }
}

// =====================================================================
// 3. Configuração de Módulos Koin (Kotlin DSL Leve)
// =====================================================================
/*
import org.koin.core.module.dsl.singleOf
import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.bind
import org.koin.dsl.module

val moduloAppKoin = module {
    // Registra Singleton: Compartilhado em toda a vida útil do App
    singleOf(::ClienteRepositoryRemoto) bind IClienteRepository::class

    // Registra Factory: Cria nova instância a cada solicitação
    factory { ServicoAuditoriaConsole() } bind IServicoAuditoria::class

    // Registra ViewModel: Ciclo de vida atrelado ao Navigation BackStack/Activity
    viewModelOf(::ClienteDetalhesViewModel)
}
*/

// =====================================================================
// 4. Equivalente em Hilt / Dagger (Anotações em Tempo de Compilação)
// =====================================================================
/*
@Module
@InstallIn(SingletonComponent::class)
abstract class ModuloHiltRepositorios {

    @Binds
    @Singleton
    abstract fun bindClienteRepository(
        impl: ClienteRepositoryRemoto
    ): IClienteRepository

    @Binds
    abstract fun bindAuditoriaService(
        impl: ServicoAuditoriaConsole
    ): IServicoAuditoria
}

@HiltViewModel
class ClienteDetalhesHiltViewModel @Inject constructor(
    private val repository: IClienteRepository,
    private val auditoria: IServicoAuditoria
) : ViewModel() {
    // ...
}
*/

// =====================================================================
// 5. Container Manual Demonstrativo (Simulador do Motor de DI)
// =====================================================================

class MiniDIContainer {
    private val singletons = mutableMapOf<Class<*>, Any>()

    fun <T : Any> registrarSingleton(classe: Class<T>, instancia: T) {
        singletons[classe] = instancia
    }

    @Suppress("UNCHECKED_CAST")
    fun <T : Any> resolver(classe: Class<T>): T {
        return singletons[classe] as? T
            ?: throw IllegalStateException("Dependência não registrada: ${classe.simpleName}")
    }
}

fun main() {
    println("=== INJEÇÃO DE DEPENDÊNCIAS: KOIN & HILT ARCHITECTURE ===")
    val container = MiniDIContainer()

    // Setup do Contêiner
    container.registrarSingleton(IClienteRepository::class.java, ClienteRepositoryRemoto())
    container.registrarSingleton(IServicoAuditoria::class.java, ServicoAuditoriaConsole())

    // Resolução e instanciação limpa sem 'new' acoplado
    val repo = container.resolver(IClienteRepository::class.java)
    val audit = container.resolver(IServicoAuditoria::class.java)
    val viewModel = ClienteDetalhesViewModel(repo, audit)

    val resultado = viewModel.carregarDados("CLI-9988")
    println("Resultado da Consulta no ViewModel: $resultado")
}