Hilt
Um pouco sobre a nova lib de Dependency Injection da Google
Sumário
O que é Injeção de Dependências?
“The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface” - Martin Fowler
Ponto importantes sobre DI:
Onde o Hilt entra nisso?
O Hilt é uma lib android para facilitar a implementação de DI de forma escalável e com desempenho.
BORA PRO CODE
Afinal, quem sabe, faz ao vivo (como não sei colei ao lado)
buildscript {
...
dependencies {
...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
android {
...
}
dependencies {
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}
Project Gradle
App Gradle
Configurando o Hilt
Crie uma classe que herde de Application e aplique a anotação @HiltAndroidApp, dessa forma o HIlt poderá ser gerado e anexado ao ciclo de vida do app.
@HiltAndroidApp
class ExampleApplication : Application() { ... }
Obs: Caso projeto seja modularizado é necessário que a classe acima tenha visibilidade dos outros módulos!
Quais classes Android o Hilt é compatível?
Exceto Application que possui sua própria anotação, todas as classes descritas acima possuem suporte a injeção através da anotação @AndroidEntryPoint
Exceções de compatibilidade para classes Android
Existe algumas restrições para a compatibilidade em classes Android, veja:
DI em classes Android
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter
...
}
Inject de campo
Passe a anotação @Inject ao campo que precisa ser injetado.
Obs: Por restrições do Hilt o campo jamais poderá ser private, caso contrário, será gerado um erro de compilação.
Definir vinculação do Hilt
Em caso de classe que não seja android é possível passar um vínculo entre as dependências dela através de injeção de construtor com a anotação @Inject
class AnalyticsAdapter @Inject constructor(
private val service: AnalyticsService
) { ... }
Nesse exemplo o Hilt precisa saber como fornecer instâncias de AnalyticsService.
Perai, como que funciona memo?
No momento de compilação o Hilt gera os componentes do Dagger, em seguida revisa os códigos cumprindo as seguintes etapas:
Módulos Hilt
Quando não se é possível injetar algo diretamente no construtor, como interfaces ou libs externas, o Módulo Hilt permite fornecer instâncias de determinados tipos.
Um módulo Hilt é uma classe anotada com @Module e com @Installin para informar em qual classe do android cada módulo será usado ou instalado.
Componentes gerados para classes do Android
Para cada classe do Android em que você pode executar a injeção de campo, há um componente Hilt associado que pode ser consultado na anotação @InstallIn. Cada componente é responsável por injetar as próprias vinculações na classe Android correspondente.
SingletonComponent | Application |
ActivityRetainedComponent | ViewModel |
ActivityComponent | Activity |
FragmentComponent | Fragment |
ViewComponent | View |
ViewWithFragmentComponent | View @WithFragmentBindings |
ServiceComponent | Service |
Injetar instâncias de interface com @Binds
A anotação @Bind informa ao Hilt, através de uma função abstrata, qual implementação usar quando se precisa de uma instancia de uma interface.
Como ficaria as classes?
O constructor-injected precisa ser fornecido para o Hilt saber como gerar as instancias de AnalyticsServiceImpl
interface AnalyticsService {
fun analyticsMethods()
}
class AnalyticsServiceImpl @Inject constructor(
...
) : AnalyticsService { ... }
Interface
Classe que implementa
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
Módulo de injeção
Como ficaria o injeção?
A injeção pelo construtor também não é possível se a classe vem de uma biblioteca externa (ex: Retrofit) ou se as instâncias precisam ser criadas com o padrão do builder, para isso existe a @Provides.
Injetar instâncias com @Provides
Na prática
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
...
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(AnalyticsService::class.java)
}
}
Módulo de injeção
No função anotada temos:
Mas e a ViewModel, onde fica?
O Hilt inclui extensões para fornecer classes de outras bibliotecas do Jetpack. Atualmente, o Hilt é compatível com os componentes ViewModel e WorkManager do Jetpack.
Mas antes de codar...
Vamos importar umas libs para poder usar ;)
...
dependencies {
...
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha01'
// When using Kotlin.
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
// When using Java.
annotationProcessor 'androidx.hilt:hilt-compiler:1.0.0-alpha01'
}
App Gradle
ViewModel
Forneça a anotação @HiltViewModel para a classe e em seu construtor aplique a anotação @Inject
Obs: A documentação oficial apresenta 2 anotações: @ViewModelInject (herdada do dagger) para o construtor e a @Assisted para o parâmetro obrigatório savedStateHandle mas ambas as anotações estão DEPRECIADAS e o parametro não é mais obrigatório
@HiltViewModel
class ExampleViewModel @Inject constructor(
private val repository: ExampleRepository
) : ViewModel() {
...
}
Na Activty/Fragment
@AndroidEntryPoint
class ExampleFragment : Fragment() {
private val viewModel: ExampleViewModel by viewModels()
...
}
Com a activity/fragment anotada com @AndroidEntryPoint receba sua viewModel através da extenção ktx viewModels()
Exemplo prático
Substituído anotações depreciadas
App sobre Rick and Morty
Utilizado JetPack Compose, Navigations, Hilt e GraphQL
The End
Muitíssimo obrigado pela
atenção e...
Guilda paga, partiu Almoço ;)