Injeção de dependência com o Hilt

O Hilt é uma biblioteca de injeção de dependência para Android que reduz a injeção manual de código boilerplate no projeto. A injeção de dependências manual exige que você construa todas as classes e dependências manualmente e use contêineres para reutilizar e gerenciar dependências.

O Hilt inclui uma maneira padrão de usar a injeção de dependências (DI, na sigla em inglês) no seu aplicativo, oferecendo contêineres para cada classe do Android no projeto e gerenciando os ciclos de vida de cada uma automaticamente. O Hilt foi criado com base na conhecida biblioteca de DI Dagger, com o objetivo de aproveitar a precisão do tempo de compilação, a performance no ambiente de execução, a escalonabilidade e o suporte do Android Studio oferecidos pelo Dagger. Para mais informações, consulte Hilt e Dagger.

Este guia explica os conceitos básicos do Hilt e os contêineres gerados. Ele também inclui uma demonstração de como inicializar um app existente para usar o Hilt.

Como adicionar dependências

Primeiro, adicione o plug-in hilt-android-gradle-plugin ao arquivo raiz build.gradle do seu projeto:

Groovy

plugins {
  ...
  id 'com.google.dagger.hilt.android' version '2.44' apply false
}

Kotlin

plugins {
  ...
  id("com.google.dagger.hilt.android") version "2.44" apply false
}

Em seguida, aplique o plug-in para Gradle e adicione estas dependências ao arquivo app/build.gradle:

Groovy

...
plugins {
  id 'kotlin-kapt'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.44"
  kapt "com.google.dagger:hilt-compiler:2.44"
}

// Allow references to generated code
kapt {
  correctErrorTypes true
}

Kotlin

plugins {
  id("kotlin-kapt")
  id("com.google.dagger.hilt.android")
}

android {
  ...
}

dependencies {
  implementation("com.google.dagger:hilt-android:2.44")
  kapt("com.google.dagger:hilt-android-compiler:2.44")
}

// Allow references to generated code
kapt {
  correctErrorTypes = true
}

O Hilt usa recursos do Java 8. Para ativar o Java 8 no seu projeto, adicione o seguinte código ao arquivo app/build.gradle:

Groovy

android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Kotlin

android {
  ...
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
  }
}

Classe de aplicativos do Hilt

Todos os apps que usam o Hilt precisam conter uma classe Application anotada com @HiltAndroidApp.

O @HiltAndroidApp aciona a geração de código do Hilt, incluindo uma classe base para seu aplicativo que serve como contêiner de dependências no nível do app.

Kotlin

@HiltAndroidApp
class ExampleApplication : Application() { ... }

Java

@HiltAndroidApp
public class ExampleApplication extends Application { ... }

Esse componente Hilt gerado é anexado ao ciclo de vida do objeto Application e fornece dependências a ele. Além disso, ele é o componente pai do app, o que significa que outros componentes podem acessar as dependências fornecidas.

Injetar dependências em classes do Android

Depois que o Hilt é configurado na classe Application e um componente no nível do aplicativo está disponível, ele pode fornecer dependências para outras classes do Android que tenham a anotação @AndroidEntryPoint:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity { ... }

Atualmente, o Hilt é compatível com as seguintes classes do Android:

  • Application (ao usar @HiltAndroidApp)
  • ViewModel (ao usar @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Se você anotar uma classe do Android com @AndroidEntryPoint, também precisará anotar as classes que dependem dela. Por exemplo, se você anotar um fragmento, também vai precisar anotar todas as atividades em que ele é usado.

O @AndroidEntryPoint gera um componente do Hilt individual para cada classe do Android no seu projeto. Esses componentes podem receber dependências das respectivas classes mãe, conforme descrito em Hierarquia de componentes.

Para receber dependências de um componente, use a anotação @Inject para realizar a injeção de campo:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

As classes injetadas pelo Hilt podem ter outras classes base que também usam a injeção. Essas classes não vão precisar da anotação @AndroidEntryPoint se forem abstratas.

Para saber em qual callback do ciclo de vida uma classe do Android é injetada, consulte Ciclos de vida dos componentes.

Definir vinculações do Hilt

Para realizar a injeção de campo, o Hilt precisa saber como fornecer instâncias das dependências necessárias do componente correspondente. Uma vinculação contém as informações necessárias para fornecer instâncias de um tipo como dependência.

Uma maneira de fornecer informações de vinculação ao Hilt é a injeção de construtor. Use a anotação @Inject no construtor de uma classe para informar ao Hilt como fornecer instâncias dessa classe:

Kotlin

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

Java

public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

Os parâmetros de um construtor anotado de uma classe são as dependências dela. No exemplo, AnalyticsAdapter tem AnalyticsService como dependência. Portanto, o Hilt também precisa saber como fornecer instâncias de AnalyticsService.

Módulos do Hilt

Às vezes, um tipo não pode ser injetado pelo construtor. Isso pode acontecer por vários motivos. Por exemplo, não é possível injetar uma interface pelo construtor. Também não é possível injetar um tipo que não é seu com o construtor, como uma classe de uma biblioteca externa. Nesses casos, forneça informações de vinculação ao Hilt usando módulos do Hilt.

Um módulo do Hilt é uma classe anotada com @Module. Como um módulo do Dagger, ele informa ao Hilt como fornecer instâncias de determinados tipos. Ao contrário dos módulos do Dagger, é preciso adicionar a anotação @InstallIn aos módulos do Hilt para informar em qual classe do Android cada módulo vai ser usado ou instalado.

As dependências fornecidas nos módulos do Hilt estão disponíveis em todos os componentes gerados associados à classe Android em que você instala o módulo.

Injetar instâncias de interface com @Binds

Considere o exemplo AnalyticsService. Se AnalyticsService for uma interface, não será possível injetá-la pelo construtor. Em vez disso, forneça as informações de vinculação criando uma função abstrata anotada com @Binds dentro de um módulo do Hilt.

A anotação @Binds informa ao Hilt qual implementação usar quando é necessário fornecer uma instância de uma interface.

A função anotada fornece as seguintes informações ao Hilt:

  • O tipo de retorno da função informa ao Hilt de qual interface essa função fornece instâncias.
  • O parâmetro de função informa ao Hilt qual implementação fornecer.

Kotlin

interface AnalyticsService {
  fun analyticsMethods()
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
class AnalyticsServiceImpl @Inject constructor(
  ...
) : AnalyticsService { ... }

@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {

  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

Java

public interface AnalyticsService {
  void analyticsMethods();
}

// Constructor-injected, because Hilt needs to know how to
// provide instances of AnalyticsServiceImpl, too.
public class AnalyticsServiceImpl implements AnalyticsService {
  ...
  @Inject
  AnalyticsServiceImpl(...) {
    ...
  }
}

@Module
@InstallIn(ActivityComponent.class)
public abstract class AnalyticsModule {

  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

O módulo do Hilt AnalyticsModule é anotado com @InstallIn(ActivityComponent.class) porque convém que o Hilt injete essa dependência em ExampleActivity. Essa anotação significa que todas as dependências em AnalyticsModule estão disponíveis em todas as atividades do app.

Injetar instâncias com @Provides

As interfaces não são o único caso em que não é possível injetar um tipo pelo construtor. A injeção pelo construtor também não é possível se a classe não for sua, porque ela vem de uma biblioteca externa (classes como Retrofit, OkHttpClient ou bancos de dados Room) ou se as instâncias precisam ser criadas com o padrão do builder.

Considere o exemplo anterior. Se você não for proprietário direto da classe AnalyticsService, poderá informar ao Hilt como fornecer instâncias desse tipo criando uma função dentro de um módulo do Hilt e a anotando com @Provides.

A função anotada fornece as seguintes informações ao Hilt:

  • O tipo de retorno da função informa ao Hilt de qual tipo essa função fornece instâncias.
  • Os parâmetros de função informam ao Hilt as dependências do tipo correspondente.
  • O corpo da função informa ao Hilt como fornecer uma instância do tipo correspondente. O Hilt executa o corpo da função sempre que precisa fornecer uma instância desse tipo.

Kotlin

@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    // Potential dependencies of this type
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
  }
}

Java

@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    // Potential dependencies of this type
  ) {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

Fornecer várias vinculações para o mesmo tipo

Nos casos em que é necessário que o Hilt forneça diferentes implementações do mesmo tipo que as dependências, você precisa fornecer várias vinculações ao Hilt. Você pode definir várias vinculações para o mesmo tipo com qualificadores.

Um qualificador é uma anotação usada para identificar uma vinculação específica para um tipo quando ele tem várias vinculações definidas.

Considere o exemplo. Se você precisar interceptar chamadas para AnalyticsService, use um objeto OkHttpClient com um interceptor (link em inglês). Para outros serviços, talvez seja necessário interceptar chamadas de outra forma. Nesse caso, informe ao Hilt como fornecer duas implementações diferentes de OkHttpClient.

Primeiro, defina os qualificadores que você usará para anotar os métodos @Binds ou @Provides:

Kotlin

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient

Java

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface AuthInterceptorOkHttpClient {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface OtherInterceptorOkHttpClient {}

Em seguida, o Hilt precisa saber como fornecer uma instância do tipo correspondente a cada qualificador. Nesse caso, é possível usar um módulo do Hilt com @Provides. Os dois métodos têm o mesmo tipo de retorno, mas os qualificadores os rotulam como duas vinculações diferentes:

Kotlin

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  fun provideAuthInterceptorOkHttpClient(
    authInterceptor: AuthInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(authInterceptor)
               .build()
  }

  @OtherInterceptorOkHttpClient
  @Provides
  fun provideOtherInterceptorOkHttpClient(
    otherInterceptor: OtherInterceptor
  ): OkHttpClient {
      return OkHttpClient.Builder()
               .addInterceptor(otherInterceptor)
               .build()
  }
}

Java

@Module
@InstallIn(ActivityComponent.class)
public class NetworkModule {

  @AuthInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideAuthInterceptorOkHttpClient(
    AuthInterceptor authInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(authInterceptor)
                   .build();
  }

  @OtherInterceptorOkHttpClient
  @Provides
  public static OkHttpClient provideOtherInterceptorOkHttpClient(
    OtherInterceptor otherInterceptor
  ) {
      return new OkHttpClient.Builder()
                   .addInterceptor(otherInterceptor)
                   .build();
  }
}

Você pode injetar o tipo específico de que precisa anotando o campo ou parâmetro com o qualificador correspondente:

Kotlin

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    @AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .client(okHttpClient)
               .build()
               .create(AnalyticsService::class.java)
  }
}

// As a dependency of a constructor-injected class.
class ExampleServiceImpl @Inject constructor(
  @AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
) : ...

// At field injection.
@AndroidEntryPoint
class ExampleActivity: AppCompatActivity() {

  @AuthInterceptorOkHttpClient
  @Inject lateinit var okHttpClient: OkHttpClient
}

Java

// As a dependency of another class.
@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    @AuthInterceptorOkHttpClient OkHttpClient okHttpClient
  ) {
      return new Retrofit.Builder()
                  .baseUrl("https://example.com")
                  .client(okHttpClient)
                  .build()
                  .create(AnalyticsService.class);
  }
}

// As a dependency of a constructor-injected class.
public class ExampleServiceImpl ... {

  private final OkHttpClient okHttpClient;

  @Inject
  ExampleServiceImpl(@AuthInterceptorOkHttpClient OkHttpClient okHttpClient) {
    this.okHttpClient = okHttpClient;
  }
}

// At field injection.
@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @AuthInterceptorOkHttpClient
  @Inject
  OkHttpClient okHttpClient;
  ...
}

Como prática recomendada, se você adicionar um qualificador a um tipo, adicione-o a todas as maneiras possíveis de fornecer essa dependência. Deixar a implementação básica ou comum sem um qualificador pode levar a erros e pode fazer com que o Hilt injete a dependência errada.

Qualificadores predefinidos no Hilt

O Hilt fornece alguns qualificadores predefinidos. Por exemplo, como você pode precisar da classe Context do aplicativo ou da atividade, o Hilt fornece os qualificadores @ApplicationContext e @ActivityContext.

Suponha que a classe AnalyticsAdapter do exemplo precise do contexto da atividade. O código a seguir demonstra como fornecer o contexto da atividade para AnalyticsAdapter:

Kotlin

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }

Java

public class AnalyticsAdapter {

  private final Context context;
  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(
    @ActivityContext Context context,
    AnalyticsService service
  ) {
    this.context = context;
    this.service = service;
  }
}

Para ver outras vinculações predefinidas disponíveis no Hilt, consulte Vinculações padrão dos componentes.

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.

Os exemplos anteriores demonstraram o uso de ActivityComponent nos módulos do Hilt.

Ele fornece os seguintes componentes:

Componente do Hilt Injetor para
SingletonComponent Application
ActivityRetainedComponent N/A
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View anotado com @WithFragmentBindings
ServiceComponent Service

Ciclos de vida dos componentes

O Hilt cria e destrói automaticamente instâncias de classes de componentes geradas seguindo o ciclo de vida das classes Android correspondentes.

Componente gerado Criado em Destruído em
SingletonComponent Application#onCreate() Application destruído
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel criado ViewModel destruído
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View destruído
ViewWithFragmentComponent View#super() View destruído
ServiceComponent Service#onCreate() Service#onDestroy()

Escopos dos componentes

Por padrão, todas as vinculações no Hilt são sem escopo. Isso significa que cada vez que seu app solicita a vinculação, o Hilt cria uma nova instância do tipo necessário.

No exemplo, sempre que o Hilt fornece AnalyticsAdapter como uma dependência para outro tipo ou por meio de injeção de campo (como em ExampleActivity), ele fornece uma nova instância de AnalyticsAdapter.

No entanto, ele também permite que uma vinculação tenha um componente específico como escopo. O Hilt só cria uma vinculação com escopo uma vez por instância do componente que é escopo da vinculação. Todas as solicitações dessa vinculação compartilham a mesma instância.

A tabela abaixo lista as anotações de escopo para cada componente gerado:

Classe do Android Componente gerado Escopo
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View anotado com @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

No exemplo, se AnalyticsAdapter for selecionado como escopo de ActivityComponent usando @ActivityScoped, o Hilt fornecerá a mesma instância de AnalyticsAdapter durante toda a vida da atividade correspondente:

Kotlin

@ActivityScoped
class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

Java

@ActivityScoped
public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

Suponha que AnalyticsService tenha um estado interno que exija que a mesma instância seja usada sempre, não só em ExampleActivity, mas em qualquer lugar do app. Nesse caso, convém aplicar o escopo de AnalyticsService ao SingletonComponent. Como resultado, a mesma instância será usada todas as vezes que o componente precisar fornecer uma instância do AnalyticsService.

O exemplo a seguir mostra como aplicar o escopo de uma vinculação a um componente em um módulo do Hilt. O escopo de uma vinculação precisa corresponder ao escopo do componente em que está instalado. Portanto, neste exemplo, é necessário instalar AnalyticsService em SingletonComponent em vez de ActivityComponent:

Kotlin

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {

  @Singleton
  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

// If you don't own AnalyticsService.
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {

  @Singleton
  @Provides
  fun provideAnalyticsService(): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService::class.java)
  }
}

Java

// If AnalyticsService is an interface.
@Module
@InstallIn(SingletonComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

// If you don't own AnalyticsService.
@Module
@InstallIn(SingletonComponent.class)
public class AnalyticsModule {

  @Singleton
  @Provides
  public static AnalyticsService provideAnalyticsService() {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

Para saber mais sobre os escopos de componentes no Hilt, consulte Escopos no Android e no Hilt.

Hierarquia dos componentes

A instalação de um módulo em um componente permite que as vinculações sejam acessadas como uma dependência de outras nesse componente ou em qualquer componente filho abaixo dele na hierarquia de componentes:

ViewWithFragmentComponent está em FragmentComponent. FragmentComponent
    e ViewComponent estão em ActivityComponent. ActivityComponent está em
    ActivityRetainComponent. ViewModelComponent está em
    ActivityKeepedComponent. ActivityKeepedComponent e ServiceComponent
    estão em SingletonComponent.
Figura 1. Hierarquia dos componentes gerados pelo Hilt.

Vinculações padrão dos componentes

Cada componente do Hilt vem com um conjunto de vinculações padrão que ele pode injetar como dependências nas próprias vinculações personalizadas. Observe que essas vinculações correspondem à atividade geral e aos tipos de fragmento, e não a subclasses específicas. Isso ocorre porque o Hilt usa uma única definição de componente de atividade para injetar todas as atividades. Cada atividade tem uma instância diferente desse componente.

Componente do Android Vinculações padrão
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application, Activity
FragmentComponent Application, Activity, Fragment
ViewComponent Application, Activity, View
ViewWithFragmentComponent Application, Activity, Fragment, View
ServiceComponent Application, Service

A vinculação de contexto do aplicativo também fica disponível ao usar @ApplicationContext. Exemplo:

Kotlin

class AnalyticsServiceImpl @Inject constructor(
  @ApplicationContext context: Context
) : AnalyticsService { ... }

// The Application binding is available without qualifiers.
class AnalyticsServiceImpl @Inject constructor(
  application: Application
) : AnalyticsService { ... }

Java

public class AnalyticsServiceImpl implements AnalyticsService {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ApplicationContext Context context) {
    this.context = context;
  }
}

// The Application binding is available without qualifiers.
public class AnalyticsServiceImpl implements AnalyticsService {

  private final Application application;

  @Inject
  AnalyticsAdapter(Application application) {
    this.application = application;
  }
}

A vinculação de contexto de atividade também fica disponível ao usar @ActivityContext. Por exemplo:

Kotlin

class AnalyticsAdapter @Inject constructor(
  @ActivityContext context: Context
) { ... }

// The Activity binding is available without qualifiers.
class AnalyticsAdapter @Inject constructor(
  activity: FragmentActivity
) { ... }

Java

public class AnalyticsAdapter {

  private final Context context;

  @Inject
  AnalyticsAdapter(@ActivityContext Context context) {
    this.context = context;
  }
}

// The Activity binding is available without qualifiers.
public class AnalyticsAdapter {

  private final FragmentActivity activity;

  @Inject
  AnalyticsAdapter(FragmentActivity activity) {
    this.activity = activity;
  }
}

Injetar dependências em classes não compatíveis com o Hilt

O Hilt é compatível com as classes mais comuns do Android. No entanto, talvez seja necessário executar a injeção de campo em classes não compatíveis com o Hilt.

Nesses casos, você pode criar um ponto de entrada usando a anotação @EntryPoint. Um ponto de entrada é o limite entre o código que é gerenciado pelo Hilt e o código que não é. É o ponto em que o código entra pela primeira vez no gráfico de objetos que o Hilt gerencia. Os pontos de entrada permitem que o Hilt use um código que não gerencia para fornecer dependências no gráfico.

Por exemplo, o Hilt não oferece suporte direto a provedores de conteúdo. Se você quiser que um provedor de conteúdo use o Hilt para receber dependências, precisará definir uma interface anotada com @EntryPoint para cada tipo de vinculação que quiser e incluir qualificadores. Em seguida, adicione @InstallIn para especificar o componente em que o ponto de entrada será instalado da seguinte maneira:

Kotlin

class ExampleContentProvider : ContentProvider() {

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface ExampleContentProviderEntryPoint {
    fun analyticsService(): AnalyticsService
  }

  ...
}

Java

public class ExampleContentProvider extends ContentProvider {

  @EntryPoint
  @InstallIn(SingletonComponent.class)
  interface ExampleContentProviderEntryPoint {
    public AnalyticsService analyticsService();
  }
  ...
}

Para acessar um ponto de entrada, use o método estático adequado de EntryPointAccessors. O parâmetro precisa ser a instância do componente ou o objeto @AndroidEntryPoint que atua como o detentor do componente. Verifique se o componente transmitido como parâmetro e o método estático EntryPointAccessors correspondem à classe do Android na anotação @InstallIn na interface @EntryPoint:

Kotlin

class ExampleContentProvider: ContentProvider() {
    ...

  override fun query(...): Cursor {
    val appContext = context?.applicationContext ?: throw IllegalStateException()
    val hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint::class.java)

    val analyticsService = hiltEntryPoint.analyticsService()
    ...
  }
}

Java

public class ExampleContentProvider extends ContentProvider {

  @Override
  public Cursor query(...) {
    Context appContext = getContext().getApplicationContext();
    ExampleContentProviderEntryPoint hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class);
    AnalyticsService analyticsService = hiltEntryPoint.analyticsService();
  }
}

Neste exemplo, use o ApplicationContext para recuperar o ponto de entrada porque ele está instalado em SingletonComponent. Se a vinculação que você quer recuperar estivesse no ActivityComponent, o ActivityContext seria usado.

Hilt e Dagger

O Hilt foi criado com base na biblioteca de injeção de dependências Dagger (em inglês), oferecendo uma maneira padrão de incorporar essa biblioteca a um app Android.

Com relação ao Dagger, as metas do Hilt são:

  • simplificar a infraestrutura relacionada ao Dagger para apps Android;
  • criar um conjunto padrão de componentes e escopos para facilitar a configuração, a legibilidade e o compartilhamento de código entre apps;
  • fornecer uma maneira fácil de provisionar diferentes vinculações a vários tipos de build, como testes, depuração ou lançamento.

Como o sistema operacional Android instancia muitas das próprias classes de framework, o uso do Dagger em um app para Android requer que você escreva uma quantidade significativa de código boilerplate. O Hilt reduz o código de texto clichê que está envolvido no uso do Dagger em um aplicativo para Android. Ele gera e fornece automaticamente o seguinte:

  • Componentes para integrar classes de framework do Android com o Dagger, que você precisaria criar manualmente.
  • Anotações de escopo a serem usadas com os componentes que o Hilt gera automaticamente.
  • Vinculações predefinidas para representar classes do Android, como Application ou Activity.
  • Qualificadores predefinidos para representar @ApplicationContext e @ActivityContext.

Os códigos do Dagger e do Hilt podem coexistir na mesma base de código. No entanto, na maioria dos casos, é melhor usar o Hilt para gerenciar todo o uso do Dagger no Android. Para migrar um projeto que usa o Dagger para o Hilt, consulte o guia de migração e Migrar o app do Dagger para o codelab do Hilt (links em inglês).

Outros recursos

Para saber mais sobre o Hilt, consulte os recursos adicionais a seguir.

Exemplos

Codelabs

Blogs