Wstrzykiwanie zależności za pomocą Hilt

Hilt to biblioteka wstrzykiwania zależności na Androidzie, która zmniejsza ilość kodu szablonowego potrzebnego do ręcznego wstrzykiwania zależności w projekcie. Ręczne wstrzykiwanie zależności wymaga ręcznego tworzenia każdej klasy i jej zależności oraz używania kontenerów do ponownego wykorzystywania zależności i zarządzania nimi.

Hilt zapewnia 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. Hilt jest oparty na popularnej bibliotece DI Dagger, dzięki czemu zapewnia poprawność w czasie kompilacji, wydajność w czasie działania, skalowalność i obsługę Androida Studio. Więcej informacji znajdziesz w sekcji Hilt i Dagger.

Z tego przewodnika dowiesz się, czym jest Hilt i jak działają generowane przez niego kontenery. Zawiera też demonstrację, jak przekształcić istniejącą aplikację, aby korzystała z Hilt.

Dodawanie zależności

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

Groovy

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

Kotlin

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

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

Groovy

...
plugins {
  id 'com.google.devtools.ksp'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.56.2"
  ksp "com.google.dagger:hilt-compiler:2.56.2"
}

Kotlin

plugins {
  id("com.google.devtools.ksp")
  id("com.google.dagger.hilt.android")
}

android {
  ...
}

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

Hilt korzysta z funkcji Javy 8. Aby włączyć w projekcie Javę 8, dodaj do pliku app/build.gradle te wiersze:

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, które korzystają z Hilt, muszą zawierać klasę Application z adnotacją @HiltAndroidApp.

@HiltAndroidApp wywołuje generowanie kodu Hilt, w tym klasy bazowej aplikacji, która służy jako kontener zależności na poziomie aplikacji.

Kotlin

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

Java

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

Ten wygenerowany komponent Hilt jest powiązany z cyklem życia obiektu Application i zapewnia mu zależności. Jest to też komponent nadrzędny aplikacji, co oznacza, że inne komponenty mogą uzyskiwać dostęp do zależności, które on udostępnia.

Wstrzykiwanie zależności do klas Androida

Gdy Hilt jest skonfigurowany w klasie Application i dostępny jest komponent na poziomie aplikacji, Hilt może dostarczać 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 (przy użyciu @HiltAndroidApp)
  • ViewModel (przy użyciu @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Jeśli dodasz adnotację @AndroidEntryPoint do klasy Androida, musisz też dodać adnotacje do klas Androida, które są od niej zależne. Jeśli na przykład 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. Komponenty te mogą otrzymywać zależności z odpowiednich klas nadrzędnych zgodnie z opisem w sekcji Hierarchia komponentów.

Aby uzyskać zależności z 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 bazowe, które również korzystają z wstrzykiwania. Jeśli te klasy są abstrakcyjne, nie wymagają adnotacji @AndroidEntryPoint.

Aby dowiedzieć się więcej o tym, w którym wywołaniu zwrotnym cyklu życia wstrzykiwana jest klasa Androida, zapoznaj się z sekcją Okresy istnienia komponentów.

Definiowanie powiązań Hilt

Aby przeprowadzić wstrzykiwanie pól, Hilt musi wiedzieć, jak dostarczać instancje niezbędnych zależności z odpowiedniego komponentu. Powiązanie zawiera informacje niezbędne do udostępniania instancji typu jako zależności.

Jednym ze sposobów przekazywania informacji o powiązaniach do Hilt jest wstrzykiwanie konstruktora. Użyj adnotacji @Inject w konstruktorze klasy, aby poinformować Hilta, jak ma dostarczać 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 są zależnościami tej klasy. W tym przykładzie AnalyticsAdapter ma AnalyticsService jako zależność. Dlatego Hilt musi też wiedzieć, jak dostarczać instancje AnalyticsService.

Moduły Hilt

Czasami typu nie można wstrzyknąć za pomocą konstruktora. Może to wynikać z wielu powodów. Nie możesz na przykład wstrzykiwać interfejsu za pomocą konstruktora. Nie możesz też wstrzykiwać przez konstruktor typu, którego nie jesteś właścicielem, np. klasy z biblioteki zewnętrznej. W takich przypadkach możesz przekazać Hiltowi informacje o powiązaniach za pomocą modułów Hilta.

Moduł Hilt to klasa oznaczona adnotacją @Module. Podobnie jak moduł Daggera informuje Hilta, jak udostępniać instancje określonych typów. W przeciwieństwie do modułów Daggera moduły Hilt muszą być oznaczone adnotacją @InstallIn, aby poinformować Hilt, w której klasie Androida każdy moduł będzie używany lub instalowany.

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

Wstrzykiwanie instancji interfejsu za pomocą adnotacji @Binds

Przyjrzyj się przykładowi AnalyticsService. Jeśli AnalyticsService jest interfejsem, nie możesz go wstrzyknąć za pomocą konstruktora. Zamiast tego przekaż Hiltowi informacje o powiązaniu, tworząc w module Hilt funkcję abstrakcyjną z adnotacją @Binds.

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

Funkcja z adnotacjami przekazuje do Hilt te informacje:

  • Typ zwracany przez funkcję informuje Hilt, jakiego interfejsu używa funkcja do udostępniania instancji.
  • Parametr funkcji informuje Hilt, którą implementację ma udostępnić.

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ż chcesz, aby Hilt wstrzykiwał tę zależność do ExampleActivity. Ta adnotacja oznacza, że wszystkie zależności w AnalyticsModule są dostępne we wszystkich aktywnościach aplikacji.

Wstrzykiwanie instancji za pomocą adnotacji @Provides

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

Weźmy pod uwagę poprzedni przykład. Jeśli nie jesteś bezpośrednim właścicielem klasy AnalyticsService, możesz poinformować Hilta, jak dostarczać instancje tego typu, tworząc funkcję w module Hilta i dodając do niej adnotację @Provides.

Funkcja z adnotacjami dostarcza Hiltowi tych informacji:

  • Typ zwracany przez funkcję informuje Hilt, jakiego typu instancje udostępnia funkcja.
  • Parametry funkcji informują Hilt o zależnościach odpowiedniego typu.
  • Ciało funkcji informuje Hilta, jak dostarczyć instancję odpowiedniego typu. Hilt wykonuje treść 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);
  }
}

Podawanie wielu powiązań dla tego samego typu

Jeśli chcesz, aby Hilt dostarczał różne implementacje tego samego typu jako zależności, musisz podać Hiltowi wiele powiązań. Możesz zdefiniować wiele powiązań tego samego typu za pomocą kwalifikatorów.

Kwalifikator to adnotacja, której używasz do identyfikowania konkretnego powiązania typu, gdy ten typ ma zdefiniowanych wiele powiązań.

Przyjrzyjmy się przykładowi. Jeśli chcesz przechwytywać wywołania AnalyticsService, możesz użyć obiektu OkHttpClientinterceptor. W przypadku innych usług może być konieczne przechwytywanie połączeń w inny sposób. W takim przypadku musisz poinformować Hilta, jak udostępniać 2 różne implementacje interfejsu OkHttpClient.

Najpierw określ 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 udostępnić instancję typu, która odpowiada każdemu kwalifikatorowi. W takim przypadku możesz użyć modułu Hilt z adnotacją @Provides. Obie metody mają ten sam typ zwracany, 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 wstawić konkretny typ, dodając do pola lub parametru odpowiedni kwalifikator:

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

Zgodnie ze sprawdzoną metodą, jeśli dodasz kwalifikator do typu, dodaj kwalifikatory do wszystkich możliwych sposobów dostarczania tej zależności. Pozostawienie podstawowej lub wspólnej implementacji bez kwalifikatora jest podatne na błędy i może spowodować wstrzyknięcie przez Hilt nieprawidłowej zależności.

Wstępnie zdefiniowane kwalifikatory w Hilt

Hilt udostępnia kilka wstępnie zdefiniowanych kwalifikatorów. Na przykład, ponieważ klasa Context może być potrzebna w aplikacji lub aktywności, Hilt udostępnia kwalifikatory Context@ActivityContext.@ApplicationContext

Załóżmy, że klasa AnalyticsAdapter z przykładu potrzebuje kontekstu aktywności. Poniższy kod pokazuje, jak przekazać 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;
  }
}

Inne predefiniowane powiązania dostępne w Hilt znajdziesz w artykule Domyślne powiązania komponentów.

Wygenerowane komponenty klas Androida

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

W poprzednich przykładach pokazaliśmy użycie ActivityComponent w modułach Hilt.

Hilt udostępnia te komponenty:

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

Okresy eksploatacji komponentów

Hilt automatycznie tworzy i usuwa instancje wygenerowanych klas komponentów, śledząc cykl życia odpowiednich klas Androida.

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

Zakresy komponentów

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

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

Hilt umożliwia jednak też ograniczenie zakresu powiązania do określonego komponentu. Hilt tworzy powiązanie o określonym zakresie tylko raz na instancję komponentu, do którego zakresu należy powiązanie, a wszystkie żądania tego powiązania korzystają z tej samej instancji.

W tabeli poniżej znajdziesz adnotacje dotyczące zakresu poszczególnych wygenerowanych komponentów:

Klasa Androida 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 ograniczysz zakres AnalyticsAdapter do ActivityComponent za pomocą @ActivityScoped, Hilt będzie udostępniać tę samą instancję AnalyticsAdapter przez cały czas trwania odpowiedniej aktywnoś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 używania tej samej instancji za każdym razem – nie tylko w ExampleActivity, ale w dowolnym miejscu w aplikacji. W takim przypadku odpowiednie jest ograniczenie zakresu AnalyticsService do SingletonComponent. W rezultacie za każdym razem, gdy komponent musi udostępnić instancję AnalyticsService, udostępnia tę samą instancję.

Poniższy przykład pokazuje, jak ograniczyć zakres powiązania do komponentu w module Hilt. Zakres powiązania musi być zgodny z zakresem komponentu, w którym jest ono zainstalowane. W tym przykładzie musisz zainstalować AnalyticsServiceSingletonComponent zamiast 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 Zakresy w Androidzie i Hilt.

Hierarchia komponentów

Zainstalowanie 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 komponencie podrzędnym w hierarchii komponentów:

Komponent ViewWithFragmentComponent znajduje się w komponencie FragmentComponent. FragmentComponent
    i ViewComponent znajdują się w sekcji ActivityComponent. ActivityComponent jest podrzędnym komponentem ActivityRetainedComponent. ViewModelComponent jest podrzędnym komponentem ActivityRetainedComponent. ActivityRetainedComponent i ServiceComponent
    należą do SingletonComponent.
Rysunek 1. Hierarchia komponentów generowanych przez Hilt.

Domyślne powiązania komponentów

Każdy komponent Hilt zawiera zestaw domyślnych powiązań, które Hilt może wstrzykiwać jako zależności do Twoich niestandardowych powiązań. Pamiętaj, że te powiązania odpowiadają ogólnym typom aktywności i fragmentów, a nie żadnej konkretnej podklasie. Wynika to z faktu, że Hilt używa jednej definicji komponentu aktywności do wstrzykiwania wszystkich aktywności. Każda aktywność ma inną instancję tego komponentu.

Komponent Androida 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ą @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 aktywności jest też dostępne za pomocą @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 najpopularniejsze klasy 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 za pomocą adnotacji @EntryPoint. Punkt wejścia to granica między kodem zarządzanym przez Hilt a kodem, który nie jest przez niego zarządzany. Jest to punkt, w którym kod po raz pierwszy wchodzi do grafu obiektów zarządzanych przez Hilt. Punkty wejścia umożliwiają Hiltowi używanie kodu, którym Hilt nie zarządza, do dostarczania zależności w grafie 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ł Hilta do uzyskiwania niektórych zależności, musisz zdefiniować interfejs z adnotacją @EntryPoint dla każdego typu powiązania, którego chcesz użyć, i uwzględnić kwalifikatory. Następnie dodaj @InstallIn, aby określić 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. Parametr powinien być instancją komponentu lub obiektem @AndroidEntryPoint, który pełni rolę kontenera 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ć ApplicationContext, aby pobrać punkt wejścia, ponieważ jest on zainstalowany w SingletonComponent. Jeśli powiązanie, które chcesz pobrać, znajduje się w ActivityComponent, użyj zamiast tego ActivityContext.

Hilt and Dagger

Hilt jest oparty na bibliotece wstrzykiwania zależności Dagger i zapewnia standardowy sposób włączania Daggera do aplikacji na Androida.

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

  • Uproszczenie infrastruktury związanej z Daggerem w przypadku aplikacji na Androida.
  • Aby utworzyć standardowy zestaw komponentów i zakresów, który ułatwi konfigurację, czytelność i udostępnianie kodu między aplikacjami.
  • Aby zapewnić łatwy sposób udostępniania różnych powiązań różnym typom kompilacji, takim jak testowa, debugowania czy produkcyjna.

System operacyjny Android tworzy instancje wielu własnych klas frameworka, więc używanie Daggera w aplikacji na Androida wymaga napisania znacznej ilości kodu szablonowego. Hilt ogranicza liczbę powtarzalnych fragmentów kodu związanych z używaniem Daggera w aplikacji na Androida. Hilt automatycznie generuje i udostępnia te elementy:

  • Komponenty do integrowania klas platformy Android z Daggerem, które w inny sposób trzeba by utworzyć ręcznie.
  • Adnotacje zakresu do użycia 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 @ApplicationContext@ActivityContext.

Kod Daggera i Hilta może współistnieć w tej samej bazie kodu. W większości przypadków najlepiej jest jednak używać Hilta do zarządzania wszystkimi przypadkami użycia Daggera na Androidzie. Jeśli chcesz przenieść projekt, który korzysta z Daggera, na Hilt, zapoznaj się z przewodnikiem po migracjisamouczkiem Migrowanie aplikacji Dagger na Hilt.

Dodatkowe materiały

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

Próbki

Codelabs

Blogi