Wstrzykiwanie zależności za pomocą Hilt

Hilt to biblioteka do wstrzykiwania zależności na Androida, która ogranicza konieczność ręcznego wstrzykiwania zależności w projekcie. Ręczne wstrzykiwanie zależności wymaga ręcznego utworzenia wszystkich klas i ich zależności oraz użycia kontenerów do ponownego wykorzystania zależności i zarządzania nimi.

Hilt udostępnia standardowy sposób korzystania z DI w aplikacji, udostępniając kontenery dla każdej klasy Androida w projekcie i automatycznie zarządzając ich cyklami życia. Platforma Hilt jest oparta na popularnej bibliotece DI (Dagger), dzięki czemu zapewnia dokładność podczas kompilowania, wydajność środowiska wykonawczego, skalowalność i obsługę Android Studio zapewnianą przez Daggera. Więcej informacji znajdziesz w artykule Hilt and Dagger.

Ten przewodnik zawiera podstawowe informacje o Hilt i generowanych przez niego kontenerach. Znajdziesz w nim też demonstrację uruchamiania istniejącej aplikacji za pomocą Hilt.

Dodawanie zależności

Najpierw dodaj wtyczkę hilt-android-gradle-plugin do głównego pliku build.gradle projektu:

Odlotowe

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

Kotlin

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

Następnie zastosuj wtyczkę Gradle i dodaj te zależności do pliku app/build.gradle:

Groovy

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

android {
  ...
}

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

// 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.51.1")
  kapt("com.google.dagger:hilt-android-compiler:2.51.1")
}

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

Hilt korzysta z funkcji Java 8. Aby włączyć Java 8 w projekcie, dodaj do pliku app/build.gradle następujący wiersz:

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
  }
}

Klasa aplikacji Hilt

Wszystkie aplikacje korzystające z Hilt muszą zawierać klasę Application, która jest opatrzona adnotacjami @HiltAndroidApp.

@HiltAndroidApp aktywuje generowanie kodu przez Hilt, w tym klasę bazową aplikacji, która pełni funkcję kontenera zależności na poziomie aplikacji.

Kotlin

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

Java

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

Ten wygenerowany komponent Hilt jest dołączony do cyklu życia obiektu Application i zawiera jego zależności. Jest też nadrzędnym komponentem aplikacji, co oznacza, że inne komponenty mogą uzyskiwać dostęp do zależności, które ona zapewnia.

Wstrzykiwanie zależności do klas Androida

Gdy Hilt zostanie skonfigurowany w klasie Application i będzie dostępny komponent na poziomie aplikacji, Hilt może udostępniać zależności innym klasom Androida, które mają adnotację @AndroidEntryPoint:

Kotlin

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

Java

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

Hilt obsługuje obecnie te klasy Androida:

  • Application (za pomocą: @HiltAndroidApp)
  • ViewModel (za pomocą @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Jeśli dodasz adnotację do klasy Androida za pomocą @AndroidEntryPoint, musisz też dodać adnotacje do klas Androida, które od niej zależą. Jeśli np. dodasz adnotację do fragmentu, musisz też dodać adnotacje do wszystkich aktywności, w których używasz tego fragmentu.

@AndroidEntryPoint generuje osobny komponent Hilt dla każdej klasy Androida w projekcie. Te komponenty mogą otrzymywać zależności z odpowiednich klas nadrzędnych zgodnie z opisem w hierarchii komponentów.

Aby uzyskać zależności od komponentu, użyj adnotacji @Inject do wykonania wstrzyknięcia pola:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Klasy, które Hilt wstrzykuje, mogą mieć inne klasy bazowe, które również korzystają z wstrzykiwania. Te klasy nie wymagają adnotacji @AndroidEntryPoint, jeśli są abstrakcyjne.

Aby dowiedzieć się więcej o tym, do którego wywołania zwrotnego cyklu życia jest wstrzykiwana klasa Androida, zapoznaj się z artykułem Cykle życia komponentów.

Zdefiniuj powiązania Hilt

Aby wykonać wstrzyknięcie pola, Hilt musi wiedzieć, jak udostępnić wystąpienia niezbędnych zależności z odpowiedniego komponentu. Binding zawiera informacje potrzebne do udostępnienia instancji typu jako zależności.

Jednym ze sposobów przekazywania informacji do Hilta jest wstrzyknięcie konstruktora. Użyj adnotacji @Inject w konstruktorze klasy, aby poinformować Hilt, jak udostępnić instancje tej klasy:

Kotlin

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

Java

public class AnalyticsAdapter {

  private final AnalyticsService service;

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

Parametry adnotowanego konstruktora klasy to zależności tej klasy. W tym przykładzie AnalyticsAdapter zawiera zależność AnalyticsService. Dlatego Hilt musi też wiedzieć, jak udostępniać instancje AnalyticsService.

Moduły Hilt

Czasami nie można wstrzyknąć typu za pomocą konstruktora. Może się tak zdarzyć z wielu powodów. Nie można na przykład wstrzyknąć interfejsu za pomocą konstruktora. Nie możesz też stosować konstruktora do wstrzykiwania typu, którego nie posiadasz, np. klasy z biblioteki zewnętrznej. W takich przypadkach możesz przekazać Hiltowi informacje wiążące, korzystając z modułów Hilt.

Moduł Hilt to klasa oznaczona @Module. Podobnie jak moduł Dagger informuje Hilt, jak udostępniać instancje określonych typów. W przeciwieństwie do modułów Dagger musisz opatrzyć moduły Hilt adnotacjami @InstallIn, aby poinformować Hilt, w której klasie Androida ma być używany lub instalowany każdy moduł.

Zależność określona w modułach Hilt jest dostępna we wszystkich wygenerowanych komponentach powiązanych z klasą Androida, w której instalujesz moduł Hilt.

Wstrzykiwanie instancji interfejsu za pomocą @Binds

Rozważ przykład AnalyticsService. Jeśli AnalyticsService jest interfejsem, nie możesz go wstrzyknąć za pomocą konstruktora. Zamiast tego podaj usłudze Hilt informacje o wiązaniu, tworząc funkcję abstrakcyjną z adnotacją @Binds w module Hilt.

Adnotacja @Binds informuje Hilt, której implementacji użyć, gdy musi udostępnić instancję interfejsu.

Funkcja z adnotacjami przekazuje Hilt te informacje:

  • Typ zwracany przez funkcję informuje Hilt, jakie interfejsy udostępnia funkcja.
  • Parametr funkcji wskazuje Hilt, jaką implementację należy podać.

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
  );
}

Moduł Hilt (AnalyticsModule) jest oznaczony adnotacją @InstallIn(ActivityComponent.class), ponieważ Hilt ma wstrzykiwać tę zależność do zbioru danych ExampleActivity. Ta adnotacja oznacza, że wszystkie zależności w AnalyticsModule są dostępne we wszystkich aktywnościach aplikacji.

Wstrzykiwanie instancji za pomocą @Provides

Interfejsy to nie jedyny przypadek, w którym nie można wstrzyknąć typu za pomocą konstruktora. Wstrzyknięcie konstruktora nie jest też możliwe, jeśli nie jesteś właścicielem klasy, ponieważ pochodzi ona z biblioteki zewnętrznej (klasy takie jak Retrofit, OkHttpClient lub bazy danych Room) lub jeśli instancje muszą być tworzone za pomocą schematu budowania.

Przeanalizuj poprzedni przykład. Jeśli nie jesteś bezpośrednim właścicielem klasy AnalyticsService, możesz poinformować Hilt, jak udostępniać instancje tego typu, tworząc funkcję w module Hilt i dodając do niej adnotacje za pomocą @Provides.

Oznaczona funkcja przekazuje Hiltowi te informacje:

  • Typ zwracany przez funkcję informuje Hilt, jakiego typu są instancje funkcji.
  • Parametry funkcji podają Hilt zależności odpowiedniego typu.
  • Ciało funkcji określa, jak Hilt ma podać wystąpienie odpowiedniego typu. Hilt wykonuje ciało funkcji za każdym razem, gdy musi dostarczyć instancję tego typu.

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);
  }
}

Podaj wiele powiązań tego samego typu

Jeśli potrzebujesz Hilta do udostępniania różnych implementacji tego samego typu jako zależności, musisz przekazać Hiltowi wiele powiązań. Za pomocą kwalifikatorów możesz zdefiniować wiele powiązań dla tego samego typu.

Kwalifikator to adnotacja, która służy do identyfikowania określonego powiązania typu, gdy dany typ ma zdefiniowane liczne powiązania.

Rozważ ten przykład. Jeśli chcesz przechwytywać wywołania funkcji AnalyticsService, możesz użyć obiektu OkHttpClient z elementem przechwytującym. W przypadku innych usług może być konieczne przechwytywanie połączeń w inny sposób. W takim przypadku musisz podać Hiltowi informacje o tym, jak udostępnić 2 różne implementacje funkcji OkHttpClient.

Najpierw zdefiniuj kwalifikatory, których użyjesz do adnotacji metod @Binds lub @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 {}

Następnie Hilt musi wiedzieć, jak podać instancję typu, która odpowiada każdemu ograniczeniu. W takim przypadku możesz użyć modułu Hilt z @Provides. Obie metody mają ten sam typ zwracanej wartości, ale kwalifikatory oznaczają je jako 2 różne typy powiązań:

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();
  }
}

Możesz wstrzyknąć konkretny typ, którego potrzebujesz, oznaczając pole lub parametr odpowiednim ogranicznikiem:

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;
  ...
}

Jeśli dodasz kwalifikator do typu, dodaj kwalifikatory do wszystkich możliwych sposobów ustanowienia tej zależności. Pozostawienie podstawowej lub popularnej implementacji bez kwalifikatora jest podatne na błędy i może skutkować wstrzyknięciem przez Hilt niewłaściwej zależności.

Wstępnie zdefiniowane kwalifikatory w Hilt

Hilt udostępnia kilka wstępnie zdefiniowanych predyktorów. Jeśli na przykład potrzebujesz klasy Context z aplikacji lub aktywności, Hilt udostępnia kwalifikatory @ApplicationContext i @ActivityContext.

Załóżmy, że klasa AnalyticsAdapter z przykładu wymaga kontekstu działania. Ten kod pokazuje, jak podać kontekst aktywności do 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;
  }
}

Informacje o innych wstępnie zdefiniowanych powiązaniach dostępnych w Hilt znajdziesz w sekcji Domyślne powiązania komponentów.

Wygenerowane komponenty klas Androida

W przypadku każdej klasy Androida, w której można wykonać wstrzyknięcie pola, istnieje powiązany komponent Hilt, do którego można się odwoływać w adnotacji @InstallIn. Każdy komponent Hilt jest odpowiedzialny za wstrzykiwanie jego powiązań do odpowiedniej klasy Androida.

W poprzednich przykładach pokazano użycie ActivityComponent w modułach Hilt.

Hilt udostępnia te komponenty:

Komponent Hilt Wstrzykiwanie do
SingletonComponent Application
ActivityRetainedComponent Nie dotyczy
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View z adnotacją @WithFragmentBindings
ServiceComponent Service

Czas życia komponentów

Hilt automatycznie tworzy i niszczy instancje wygenerowanych klas komponentów zgodnie z cyklem życia odpowiednich klas Androida.

Wygenerowany komponent Utworzono o Zniszczono
SingletonComponent Application#onCreate() Application zniszczone
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent Utworzono ViewModel Zniszczono: ViewModel
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View zniszczone
ViewWithFragmentComponent View#super() Zniszczono: View
ServiceComponent Service#onCreate() Service#onDestroy()

Zakresy komponentów

Domyślnie wszystkie powiązania w Hilt są nieskoordynowane. Oznacza to, że za każdym razem, gdy aplikacja żąda powiązania, Hilt tworzy nową instancję odpowiedniego typu.

W tym przykładzie za każdym razem, gdy Hilt udostępnia AnalyticsAdapter jako zależność innego typu lub za pomocą wstrzyknięcia pola (jak w ExampleActivity), udostępnia nowy egzemplarz AnalyticsAdapter.

Hilt umożliwia też ograniczenie zakresu powiązania do konkretnego komponentu. Hilt tworzy ograniczone wiązanie tylko raz na instancję komponentu, do którego jest ono ograniczone, a wszystkie żądania tego wiązania korzystają z tej samej instancji.

Tabela poniżej zawiera adnotacje zakresu dla każdego wygenerowanego komponentu:

Klasa Android Wygenerowany komponent Zakres
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View z adnotacją @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

W tym przykładzie, jeśli zawęzisz zakres AnalyticsAdapter do ActivityComponent za pomocą @ActivityScoped, Hilt udostępnia ten sam element AnalyticsAdapter przez cały czas trwania odpowiedniej czynności:

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;
  }
  ...
}

Załóżmy, że AnalyticsService ma stan wewnętrzny, który wymaga, aby za każdym razem używano tego samego wystąpienia, nie tylko w ExampleActivity, ale w dowolnym miejscu w aplikacji. W tym przypadku odpowiednie jest ograniczenie zakresu AnalyticsService do SingletonComponent. W rezultacie, gdy komponent musi udostępnić instancję AnalyticsService, zawsze udostępnia tę samą instancję.

Przykład poniżej pokazuje, jak określić zakres powiązania do komponentu w module Hilt. Zakres powiązania musi być zgodny z zakresem komponentu, w którym jest zainstalowany. W tym przykładzie musisz zainstalować AnalyticsServiceSingletonComponent zamiast 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);
  }
}

Więcej informacji o zakresie działania komponentów Hilt znajdziesz w artykule Zapisywanie zakresu działania w Androidzie i Hilt.

Hierarchia komponentów

Instalowanie modułu w komponencie umożliwia dostęp do jego powiązań jako zależności innych powiązań w tym komponencie lub w dowolnym podrzędnym komponencie w hierarchii komponentów:

ViewWithFragmentComponent znajduje się w komponencie FragmentComponent. FragmentComponent i ViewComponent znajdują się w ActivityComponent. Komponent Activity się znajduje się w sekcji ActivityPause Komponent. ViewModelComponent jest podklasą ActivityRetainedComponent. Parametr ActivityKeeped Komponent i Service Komponent znajduje się w sekcji Singleton Komponent.
Rysunek 1. Hierarchia komponentów generowanych przez Hilta.

Powiązania domyślne komponentów

Każdy komponent Hilt jest dostarczany z zestawem domyślnych powiązań, które Hilt może wstrzyknąć jako zależności do własnych niestandardowych powiązań. Pamiętaj, że te powiązania odpowiadają ogólnej aktywności i typom fragmentów, a nie konkretnej podklasie. Dzieje się tak, ponieważ Hilt używa jednej definicji komponentu aktywności do wstrzykiwania wszystkich aktywności. Każda aktywność ma inne wystąpienie tego komponentu.

Komponent Android Domyślne powiązania
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

Powiązanie kontekstu aplikacji jest też dostępne za pomocą funkcji @ApplicationContext. Na przykład:

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;
  }
}

Powiązanie kontekstu działania jest też dostępne za pomocą funkcji @ActivityContext. Przykład:

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;
  }
}

Wstrzykiwanie zależności w klasach nieobsługiwanych przez Hilt

Hilt obsługuje większość typowych klas Androida. Może jednak być konieczne wykonanie wstrzyknięcia pola w klasach, których Hilt nie obsługuje.

W takich przypadkach możesz utworzyć punkt wejścia za pomocą adnotacji @EntryPoint. Punkt wejścia to granica między kodem zarządzanym przez Hilta a kodem, który nie jest zarządzany przez Hilta. Jest to punkt, w którym kod po raz pierwszy wchodzi do grafu obiektów zarządzanych przez Hilt. Punkty wejścia pozwalają Hilt na użycie kodu, którym Hilt nie zarządza do udostępniania zależności w obrębie grafu zależności.

Na przykład Hilt nie obsługuje bezpośrednio dostawców treści. Jeśli chcesz, aby dostawca treści używał Hilt do uzyskiwania niektórych zależności, musisz zdefiniować interfejs z adnotacjami @EntryPoint dla każdego typu powiązania, który chcesz uwzględnić, oraz dodać odpowiednie kwalifikatory. Następnie dodaj @InstallIn, aby określić komponent, w którym chcesz zainstalować punkt wejścia:

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();
  }
  ...
}

Aby uzyskać dostęp do punktu wejścia, użyj odpowiedniej metody statycznej z EntryPointAccessors. Ten parametr powinien być instancją komponentu lub obiektem @AndroidEntryPoint, który pełni rolę posiadacza komponentu. Upewnij się, że komponent przekazywany jako parametr i statyczna metoda EntryPointAccessors pasują do klasy Androida w adnotacji @InstallIn w interfejsie @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();
  }
}

W tym przykładzie musisz użyć funkcji ApplicationContext, aby pobrać punkt wejścia, ponieważ jest on zainstalowany w elementach SingletonComponent. Jeśli binding, które chcesz pobrać, znajduje się w ActivityComponent, użyj polecenia ActivityContext.

Hilt and Dagger

Hilt jest oparty na bibliotece wstrzykiwania zależności od Daggera, stanowiąc standardowy sposób włączania Daggera do aplikacji na Androida.

W odniesieniu do Daggera cele Hilta są następujące:

  • Upraszczanie infrastruktury związanej z Daggerem w przypadku aplikacji na Androida.
  • Aby utworzyć standardowy zestaw komponentów i zakresów, który ułatwia konfigurację, czytelność i udostępnianie kodu między aplikacjami.
  • Aby ułatwić tworzenie różnych powiązań dla różnych typów kompilacji, takich jak testowanie, debugowanie czy wersja.

System operacyjny Android tworzy wiele własnych klas frameworka, więc korzystanie z Daggera w aplikacji na Androida wymaga napisania dużej ilości kodu stałego. Hilt zmniejsza ilość kodu stałego używanego do korzystania z Daggera w aplikacji na Androida. Hilt automatycznie generuje i zapewnia te funkcje:

  • Komponenty do integracji klas frameworka Androida z Daggerem, które w przeciwnym razie trzeba by utworzyć ręcznie.
  • Adnotacje zakresu do stosowania w przypadku komponentów generowanych automatycznie przez Hilt.
  • Wstępnie zdefiniowane powiązania służące do reprezentowania klas Androida, takich jak Application lub Activity.
  • Wstępnie zdefiniowane kwalifikatory reprezentujące @ApplicationContext@ActivityContext.

Kod sztyletu i kod Hilt mogą współistnieć w tej samej bazie kodu. W większości przypadków najlepiej jednak używać Hilt do zarządzania wszystkimi zastosowaniami Daggera na Androidzie. Aby przenieść projekt, który używa narzędzia Dagger do Hilt, zapoznaj się z przewodnikiem po migracji i artykułem Migracja aplikacji Dagger do Hilt (ćwiczenie z programowania).

Dodatkowe materiały

Więcej informacji o Hilt znajdziesz w tych dodatkowych materiałach.

Próbki

Ćwiczenia z programowania

Blogi