Injecao de dependencias no Android: Hilt vs Koin - Guia completo e perguntas de entrevista 2026

Comparacao detalhada entre Hilt e Koin para injecao de dependencias no Android: exemplos de codigo, benchmarks de desempenho e perguntas frequentes em entrevistas tecnicas de 2026.

Hilt vs Koin - Injecao de dependencias Android

A injecao de dependencias determina como as classes recebem suas dependencias em vez de cria-las diretamente. No ecossistema Android de 2026, dois frameworks dominam o cenario: Hilt (a solucao compile-time do Google construida sobre o Dagger) na versao 2.57.1, e Koin (um service locator leve que opera em tempo de execucao) na versao 4.2.1. A escolha entre ambos afeta tempos de compilacao, desempenho na inicializacao, testabilidade e a velocidade com que novos desenvolvedores sao integrados ao time.

DI em compile-time vs runtime

O Hilt valida todo o grafo de dependencias durante a compilacao e falha o build caso alguma vinculacao esteja ausente. O Koin resolve dependencias em tempo de execucao usando um DSL Kotlin, capturando erros de configuracao apenas quando o caminho de codigo afetado e executado. Essa diferenca fundamental determina todos os trade-offs entre os dois frameworks.

Como o Hilt funciona internamente

O Hilt gera componentes Dagger mapeados para classes do ciclo de vida do Android. A anotacao @HiltAndroidApp dispara a geracao de codigo em tempo de compilacao, produzindo factories e providers para cada vinculacao declarada. O KSP (Kotlin Symbol Processing) substituiu o KAPT como processador de anotacoes recomendado a partir do Hilt 2.48, reduzindo o tempo de processamento pela metade.

Uma configuracao tipica do Hilt envolve tres pecas: um modulo declarando as vinculacoes, um ponto de entrada (Activity, Fragment ou ViewModel) e a classe Application anotada com @HiltAndroidApp.

AppModule.ktkotlin
@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Provides
    @Singleton
    fun provideUserRepository(
        retrofit: Retrofit
    ): UserRepository {
        return UserRepositoryImpl(retrofit.create(UserApi::class.java))
    }
}
UserViewModel.ktkotlin
@HiltViewModel
class UserViewModel @Inject constructor(
    private val userRepository: UserRepository
) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    fun loadUsers() {
        viewModelScope.launch {
            _users.value = userRepository.getUsers()
        }
    }
}

O compilador verifica que UserRepository possui um provider valido antes mesmo da aplicacao ser executada. Vinculacoes ausentes se manifestam como erros de compilacao, nao como crashes em producao.

Configuracao do Koin e a abordagem DSL Kotlin

O Koin segue a direcao oposta: zero geracao de codigo, zero processamento de anotacoes. As dependencias sao declaradas em Kotlin puro usando um DSL, e o framework as resolve em tempo de execucao por meio de um registro global de servicos. O Koin 4.2 introduziu modulos lazy para carregamento paralelo na inicializacao e um novo motor CoreResolverV2 que otimiza a resolucao de escopos.

AppModule.ktkotlin
val appModule = module {
    // Singleton Retrofit instance
    single<Retrofit> {
        Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    // Singleton repository bound to its interface
    single<UserRepository> {
        UserRepositoryImpl(get<Retrofit>().create(UserApi::class.java))
    }

    // ViewModel with injected repository
    viewModel { UserViewModel(get()) }
}
MyApplication.ktkotlin
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApplication)
            modules(appModule)
        }
    }
}

O DSL e lido de forma natural e nao requer nenhum plugin de processamento de anotacoes na configuracao do Gradle. O custo: um erro de digitacao na resolucao get() so aparece em tempo de execucao quando o ponto de injecao afetado e acionado.

Benchmarks de desempenho: tempo de build vs custo em runtime

A questao do desempenho aparece em praticamente toda entrevista tecnica Android. Numeros concretos valem mais do que opinioes.

| Metrica | Hilt 2.57 (KSP) | Koin 4.2 | |---------|-----------------|----------| | Overhead build limpo | +8-15s (geracao KSP) | ~0s (sem geracao) | | Overhead build incremental | +2-4s | ~0s | | Inicializacao app (50 bindings) | ~0ms (codigo pre-gerado) | ~5-15ms (resolucao do grafo) | | Inicializacao app (500 bindings) | ~0ms | ~30-80ms | | Impacto no tamanho do APK | +200-400 KB (codigo gerado) | +100 KB (biblioteca runtime) | | Deteccao de vinculacao ausente | Erro de compilacao | Crash em runtime |

Para projetos com menos de 100 bindings, o overhead de runtime do Koin permanece imperceptivel. Acima de 300 bindings, o custo na inicializacao se torna mensuravel, embora os modulos lazy do Koin 4.2 mitiguem isso ao paralelizar o carregamento dos modulos.

KSP vs KAPT

Migrar de KAPT para KSP no processamento do Hilt reduz o tempo de processamento de anotacoes em 40 a 60%. Qualquer projeto que ainda use KAPT com Hilt deve migrar para KSP imediatamente. O Hilt suporta KSP desde a versao 2.48.

Estrategias de teste com cada framework

A testabilidade e onde as diferencas arquiteturais se tornam praticas. O Hilt oferece @TestInstallIn para substituir modulos de producao por test doubles em tempo de compilacao. O Koin disponibiliza loadKoinModules() para sobreescrever definicoes em tempo de execucao.

kotlin
// Hilt test module — replaces AppModule bindings in tests
@Module
@TestInstallIn(
    components = [SingletonComponent::class],
    replaces = [AppModule::class]
)
object FakeAppModule {

    @Provides
    @Singleton
    fun provideUserRepository(): UserRepository {
        return FakeUserRepository() // In-memory test double
    }
}
kotlin
// Koin test — override at runtime
class UserViewModelTest : KoinTest {

    @Before
    fun setUp() {
        startKoin {
            modules(
                module {
                    single<UserRepository> { FakeUserRepository() }
                    viewModel { UserViewModel(get()) }
                }
            )
        }
    }

    @Test
    fun `loads users from repository`() {
        val viewModel: UserViewModel = get()
        viewModel.loadUsers()
        assertEquals(3, viewModel.users.value.size)
    }

    @After
    fun tearDown() {
        stopKoin()
    }
}

Os testes com Hilt detectam modulos de teste mal configurados em tempo de compilacao. Os testes com Koin exigem gerenciamento cuidadoso do ciclo de vida (startKoin/stopKoin), mas oferecem mais flexibilidade para sobrecargas parciais de modulos.

Pronto para mandar bem nas entrevistas de Android?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Arquitetura multi-modulo: escalando DI entre features

Projetos Android de grande porte distribuem funcionalidades em modulos Gradle separados. O framework de DI precisa lidar com as fronteiras entre modulos de forma limpa.

Com o Hilt, cada modulo de feature declara seu proprio @Module anotado com @InstallIn. O Hilt mescla todos os modulos em uma hierarquia unica de componentes em tempo de compilacao. A arquitetura MVVM combina naturalmente com os componentes com escopo do Hilt.

feature-payments/PaymentModule.ktkotlin
@Module
@InstallIn(ViewModelComponent::class)
object PaymentModule {

    @Provides
    fun providePaymentGateway(
        retrofit: Retrofit // Provided by :app module
    ): PaymentGateway {
        return StripePaymentGateway(retrofit.create(PaymentApi::class.java))
    }
}

Com o Koin, os modulos de feature exportam suas declaracoes module {}, e o modulo da aplicacao carrega todos na inicializacao. Os modulos lazy do Koin 4.2 permitem carregamento adiado dos modulos de feature ate o primeiro acesso.

feature-payments/paymentModule.ktkotlin
val paymentModule = module {
    factory<PaymentGateway> {
        StripePaymentGateway(get<Retrofit>().create(PaymentApi::class.java))
    }
}

// app/MyApplication.kt — loads all feature modules
startKoin {
    modules(appModule, paymentModule, analyticsModule)
}

O Hilt impoe a visibilidade das dependencias por meio dos escopos de componentes. O Koin se baseia em convencoes: qualquer modulo pode acessar as definicoes de qualquer outro modulo, o que proporciona flexibilidade, mas pode gerar acoplamento implicito.

Consideracoes sobre Kotlin Multiplatform

O Koin suporta Kotlin Multiplatform (KMP) nativamente. O mesmo DSL module {} funciona no Android, iOS, Desktop e alvos Web. O Hilt e exclusivo do Android, pois depende de componentes de ciclo de vida especificos da plataforma e do processador de anotacoes do Dagger.

Para projetos que visam multiplas plataformas, o Koin e a unica opcao viavel entre os dois. Modulos de logica de negocio compartilhada podem declarar suas dependencias uma unica vez e injeta-las em todos os alvos.

Perguntas frequentes de entrevista sobre DI no Android

Estas perguntas aparecem regularmente em entrevistas tecnicas Android. Cada resposta se mantem concisa e foca no que os entrevistadores esperam.

P: Qual e a diferenca entre injecao de dependencias em compile-time e runtime?

A injecao em compile-time (Hilt/Dagger) gera o codigo de injecao durante o build. O compilador valida todo o grafo de dependencias, capturando vinculacoes ausentes antes da execucao. A injecao em runtime (Koin) resolve dependencias quando sao solicitadas pela primeira vez, utilizando um padrao de registro de servicos. A injecao em compile-time produz uma inicializacao mais rapida, porem builds mais lentos; a injecao em runtime nao impacta o build, mas adia a deteccao de erros.

P: Quais escopos de componentes Hilt existem e como se mapeiam aos ciclos de vida do Android?

SingletonComponent persiste durante toda a vida da aplicacao. ActivityRetainedComponent sobrevive a mudancas de configuracao. ViewModelComponent tem escopo vinculado ao ciclo de vida de um ViewModel. ActivityComponent, FragmentComponent e ViewComponent seguem seus respectivos lifecycle owners do Android. Escopos personalizados podem estender essa hierarquia.

P: Como o Koin lida com a injecao de ViewModel no Jetpack Compose?

O Koin fornece koinViewModel() como funcao Composable que cria ou recupera um ViewModel com escopo no ViewModelStoreOwner mais proximo. Desde o Koin 4.2, koinNavViewModel() vincula o escopo dos ViewModels as entradas do grafo de navegacao ao usar a navegacao do Jetpack Compose.

P: Em quais situacoes o Koin seria uma escolha melhor do que o Hilt?

O Koin se encaixa melhor em projetos KMP (o Hilt e exclusivo do Android), aplicacoes de pequeno a medio porte onde o tempo de build importa mais que o tempo de inicializacao, e equipes que preferem um DSL Kotlin explicito em vez de configuracao baseada em anotacoes. Prototipos e provas de conceito tambem se beneficiam da configuracao minima do Koin.

Erro comum em entrevistas

Chamar o Koin de "framework de injecao de dependencias" e tecnicamente impreciso. O Koin e um service locator: as dependencias sao obtidas via get() em vez de injetadas via construtor. Entrevistadores familiarizados com essa distincao esperam que os candidatos a reconhecam. Hilt/Dagger realiza verdadeira injecao de dependencias por meio de chamadas a construtores geradas.

Framework de decisao: escolhendo entre Hilt e Koin

| Fator | Hilt | Koin | |-------|------|------| | Validacao do grafo | Compile-time | Runtime | | Impacto no tempo de build | Maior (codegen KSP) | Nenhum | | Custo de inicializacao | Quase zero | Escala com o tamanho do grafo | | Curva de aprendizado | Mais ingreme (conceitos Dagger) | Suave (DSL Kotlin puro) | | Suporte KMP | Nao | Sim | | Integracao Jetpack | Profunda (Google oficial) | Boa (comunidade) | | Escalabilidade do time | Mais solida (escopos impostos) | Flexivel (baseada em convencoes) | | Testes | @TestInstallIn (compile-safe) | loadKoinModules (runtime) |

Nenhum dos frameworks e universalmente superior. A escolha depende do tamanho do projeto, da experiencia do time e das plataformas-alvo.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Conclusao

  • O Hilt 2.57 com KSP entrega validacao do grafo em compile-time e custo de inicializacao quase zero, posicionando-se como a escolha mais solida para grandes projetos Android de plataforma unica com requisitos rigorosos de confiabilidade
  • O Koin 4.2 com modulos lazy e CoreResolverV2 reduz a diferenca de desempenho na inicializacao enquanto mantem zero impacto no build e compatibilidade completa com KMP
  • Ambos os frameworks lidam com arquiteturas multi-modulo, mas o Hilt impoe limites de escopo em compile-time enquanto o Koin depende de convencoes do time
  • Para a preparacao para entrevistas Android, entender o trade-off entre compile-time e runtime e saber articular quando cada framework se encaixa melhor e a base esperada
  • Migrar entre frameworks nao e trivial: a escolha inicial deve considerar a trajetoria do projeto a longo prazo em termos de plataformas e escalabilidade

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Tags

#android
#hilt
#koin
#dependency-injection
#kotlin

Compartilhar

Artigos relacionados