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ę Androida Studio, które zapewnia Dagger. Więcej informacji znajdziesz w artykule na temat korporacji.

W tym przewodniku wyjaśniamy podstawowe pojęcia dotyczące Hilt i jego generowanych kontenerów. Znajdziesz w nim też demonstrację uruchamiania istniejącej aplikacji w celu korzystania z 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.44' apply false
}

Kotlin

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

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

Odlotowe

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

Hilt korzysta z funkcji Java 8. Aby włączyć środowisko Java 8 w projekcie, dodaj do pliku app/build.gradle ten kod:

Odlotowe

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 używające Hilt muszą zawierać klasę Application z adnotacją @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łączany do cyklu życia obiektu Application i udostępnia mu zależności. Jest to również nadrzędny komponent aplikacji, co oznacza, że inne komponenty mogą korzystać z zależności od niej.

Wstrzykiwanie zależności do klas Androida

Gdy Hilt zostanie skonfigurowany w klasie Application i uzyskasz dostęp do komponentu na poziomie aplikacji, może ona przekazywać zależności do innych klas 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 oznaczysz klasę Androida za pomocą @AndroidEntryPoint, musisz też dodać adnotacje do klas Androida, które są od niej zależne. Jeśli na przykład dodajesz adnotacje do fragmentu, musisz też dodać adnotacje do wszystkich działań, w których go używasz.

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

Aby uzyskać zależności od komponentu, użyj adnotacji @Inject do wstrzykiwania pól:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Klasy wstrzykiwane przez Hilt mogą mieć inne klasy podstawowe, które również używają wstrzykiwania. Te klasy nie potrzebują adnotacji @AndroidEntryPoint, jeśli są abstrakcyjne.

Więcej informacji o wywołaniu zwrotnym cyklu życia wstrzykiwanej klasy Androida znajdziesz w artykule Okresy życia komponentów.

Zdefiniuj powiązania Hilt

Aby wykonać wstrzykiwanie pól, Hilt musi wiedzieć, jak udostępnić wystąpienia niezbędnych zależności z odpowiedniego komponentu. Wiązanie zawiera informacje niezbędne do ustanawiania wystąpień określonego typu jako zależności.

Jednym ze sposobów na przekazanie informacji o powiązaniu do Hilt jest wstrzykiwanie przez 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 konstruktora klasy z adnotacjami to zależności tej klasy. W tym przykładzie AnalyticsAdapter zawiera zależność AnalyticsService. Dlatego Hilt musi też wiedzieć, jak udostępniać wystąpienia AnalyticsService.

Moduły Hilt

Czasami typ nie może być wstrzykiwany przez konstruktor. Może się tak zdarzyć z wielu powodów. Na przykład nie można wstawić interfejsu przez konstruktor. Nie możesz też wstrzykiwać typu, który nie należy do Ciebie, na przykład klasy z biblioteki zewnętrznej. W takich przypadkach możesz przekazać informacje o wiązaniu za pomocą modułów Hilt.

Moduł Hilt to klasa oznaczona adnotacją @Module. Podobnie jak moduł Dagger, informuje on Hilt, jak udostępniać instancje określonych typów. W przeciwieństwie do modułów sztyletu do modułów Hilt trzeba dodać atrybut @InstallIn, aby wskazać Hilt, w jakiej klasie Androida dany moduł będzie używany lub zainstalowany.

Zależności podane w modułach Hilt są dostępne we wszystkich wygenerowanych komponentach powiązanych z klasą Androida, w której został zainstalowany moduł Hilt.

Wstrzykiwanie instancji interfejsu za pomocą @Binds

Przyjrzyjmy się przykładowi AnalyticsService. Jeśli AnalyticsService to interfejs, nie możesz go wstawić przez konstruktor. Zamiast tego podaj usłudze Hilt informacje o powiązaniu, tworząc funkcję abstrakcyjną z adnotacją @Binds w module Hilt.

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

Funkcja z adnotacjami przekazuje Hilt te informacje:

  • Typ zwracania funkcji informuje Hilt o tym, z jakiego interfejsu funkcja udostępnia wystąpienia.
  • Parametr funkcji informuje Hilt, którą implementację wyświetlić.

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 AnalyticsModule są dostępne we wszystkich działaniach w aplikacji.

Wstrzykiwanie instancji za pomocą @Provides

Interfejsy nie są jedynym przypadkiem, w którym nie można wstawić typu przez konstruktor. Wstrzyknięcie konstruktora jest też niemożliwe, jeśli nie jesteś właścicielem klasy, ponieważ pochodzi ona z biblioteki zewnętrznej (klasy takie jak Retrofit, OkHttpClient czy bazy danych sal) lub jeśli instancje muszą być tworzone za pomocą wzorca kreatora.

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

Funkcja z adnotacjami przekazuje do Hilt te informacje:

  • Typ zwracany funkcji informuje Hilta, jakiego typu wystąpienia funkcja generuje.
  • Parametry funkcji informują Hilt o zależności odpowiedniego typu.
  • Treść funkcji informuje Hilt o tym, jak udostępnić wystąpienie odpowiedniego typu. Hilt wykonuje treść funkcji za każdym razem, gdy musi udostępnić 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 chcesz, aby usługa Hilt udostępniała różne implementacje tego samego typu jako zależności, musisz dostarczyć Hiltowi wiele powiązań. Za pomocą kwalifikatorów możesz zdefiniować wiele powiązań dla tego samego typu.

Kwalifikator to adnotacja służąca do identyfikowania konkretnego powiązania dla typu, gdy ten typ ma zdefiniowanych wiele powiązań.

Przeanalizujmy 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 konieczne może być przechwytywanie połączeń w inny sposób. W takim przypadku musisz poinformować Hilt, jak udostępnić 2 różne implementacje polecenia OkHttpClient.

Najpierw zdefiniuj kwalifikatory, których będziesz używać do dodawania adnotacji do 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 udostępnić wystąpienie typu odpowiadającego każdemu kwalifikatorowi. W takim przypadku możesz użyć modułu Hilt z narzędziem @Provides. Obie metody mają ten sam zwracany typ, ale kwalifikatory oznaczają je jako 2 różne powiązania:

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 kwalifikatorem:

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 kwalifikatoró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

Każda klasa Androida, w której można wykonać wstrzykiwanie pól, ma powiązany komponent Hilt, do którego można się odwołać w adnotacji @InstallIn. Każdy komponent Hilt jest odpowiedzialny za wstrzykiwanie jego powiązań do odpowiedniej klasy Androida.

Poprzednie przykłady pokazują wykorzystanie ActivityComponent w modułach Hilt.

Hilt udostępnia te komponenty:

Komponent z trzonkiem Wtryskiwacz do
SingletonComponent Application
ActivityRetainedComponent Nie dotyczy
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View ma adnotację z: @WithFragmentBindings
ServiceComponent Service

Czasy eksploatacji komponentów

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

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

Zakresy komponentów

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

W tym przykładzie za każdym razem, gdy Hilt udostępnia zależność AnalyticsAdapter jako zależność od innego typu lub przez wstrzyknięcie pól (np. ExampleActivity), Hilt udostępnia nowe wystąpienie AnalyticsAdapter.

Hilt umożliwia jednak również ograniczenie powiązania do konkretnego komponentu. Hilt tworzy powiązanie zakresu tylko raz na każdą instancję komponentu, do którego zakres jest ograniczony, a wszystkie żądania tego powiązania korzystają z tej samej instancji.

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

Zajęcia dotyczące Androida Wygenerowany komponent Zakres
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View ma adnotację z: @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

W tym przykładzie, jeśli ograniczysz AnalyticsAdapter do ActivityComponent za pomocą @ActivityScoped, Hilt zapewni to samo wystąpienie AnalyticsAdapter w całym cyklu życia danego działania:

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

Przyjmijmy, że AnalyticsService ma stan wewnętrzny, który wymaga, aby za każdym razem ta sama instancja była używana – nie tylko w ExampleActivity, ale w dowolnym miejscu w aplikacji. W takim przypadku należy zastosować zakres AnalyticsService do obiektu SingletonComponent. W efekcie za każdym razem, gdy komponent musi udostępniać instancję AnalyticsService, za każdym razem 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 odpowiadać zakresowi komponentu, w którym zostało zainstalowane, więc w tym przykładzie musisz zainstalować AnalyticsService w SingletonComponent, a nie w 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 zakresach komponentów Hilt znajdziesz w artykule na temat określania zakresu w Androidzie i Hilt.

Hierarchia komponentów

Gdy zainstalujesz moduł w komponencie, uzyskasz dostęp do jego powiązań w zależności od innych powiązań tego komponentu lub dowolnego komponentu podrzędnego w hierarchii komponentów:

ViewWithFragmentKomponent znajduje się w sekcji FragmentKomponent. Fragment Komponent i Komponent Widok znajdują się w sekcji Aktywność. Komponent Activity się znajduje się w sekcji ActivityStandiony. ViewModelKomponent znajduje się w sekcji Aktywność zatrzymana. Komponent ActivityKeeped Komponent i Komponent Servicemont
 znajdują się w sekcji Singleton Komponent.
Rysunek 1. Hierarchia komponentów generowanych przez Hilta.

Domyślne powiązania komponentu

Każdy komponent Hilt zawiera zestaw domyślnych powiązań, które Hilt może wstrzykiwać jako zależności do Twoich własnych wiązań niestandardowych. 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 działań. Każda aktywność ma inną instancję tego komponentu.

Komponent Androida Powiązania domyślne
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ść popularnych klas aplikacji na Androida. Może jednak być konieczne wstrzykiwanie pól w klasach, których Hilt nie obsługuje.

W takich przypadkach możesz utworzyć punkt wejścia przy użyciu adnotacji @EntryPoint. Punkt wejścia to granica między kodem zarządzanym przez Hilt a kodem, który nie jest. To punkt, w którym kod najpierw zostaje umieszczony na wykresie obiektów zarządzanych przez Hilta. 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 wykorzystywał Hilt do uzyskiwania niektórych zależności, musisz zdefiniować interfejs z adnotacją @EntryPoint dla każdego wybranego typu powiązania i uwzględnić kwalifikatory. Następnie dodaj @InstallIn, aby określić w ten sposób komponent, w którym ma zostać zainstalowany 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 funkcję właściciela komponentu. Upewnij się, że komponent przekazywany jako parametr i metoda statyczna EntryPointAccessors są zgodne z klasą 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ć ApplicationContext, aby pobrać punkt wejścia, ponieważ jest on zainstalowany w SingletonComponent. Jeśli powiązanie, które chcesz pobrać, znajduje się w ActivityComponent, zamiast tego użyj ActivityContext.

Krzyżyk i krzyżyk

Hilt jest oparty na bibliotece wstrzykiwania zależności od Daggera, co pozwala w standardowy sposób włączyć Daggera w aplikację na Androida.

W przypadku Daggera cele Hilt są następujące:

  • Aby uprościć infrastrukturę aplikacji na Androida związaną z daggerem.
  • Utworzenie standardowego zestawu komponentów i zakresów w celu ułatwienia konfiguracji, czytelności i udostępniania kodu między aplikacjami.
  • Aby ułatwić udostępnianie różnych powiązań do różnych typów kompilacji, na przykład przez testowanie, debugowanie czy wdrażanie.

System operacyjny Android tworzy instancje wielu własnych klas platformy, dlatego korzystanie z Diggera w aplikacji na Androida wymaga napisania dużej ilości gotowych tekstów. Hilt redukuje powtarzalny kod związany z używaniem Daggera w aplikacji na Androida. Hilt automatycznie generuje i dostarcza:

  • Komponenty do integrowania klas platformy Androida z Daggerem, które w innym przypadku trzeba by utworzyć ręcznie.
  • Adnotacje dotyczące zakresu – do wykorzystania z komponentami generowanymi automatycznie przez Hilt.
  • Wstępnie zdefiniowane powiązania reprezentujące klasy Androida, takie jak Application lub Activity.
  • Wstępnie zdefiniowane kwalifikatory reprezentujące wartości @ApplicationContext i @ActivityContext.

Kod sztyletu i kod Hilt mogą współistnieć w tej samej bazie kodu. Jednak w większości przypadków najlepiej jest używać Hilt do zarządzania całym wykorzystaniem 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

Aby dowiedzieć się więcej o Hilt, zapoznaj się z dodatkowymi materiałami poniżej.

Próbki

Ćwiczenia z programowania

Blogi