Wstrzykiwanie zależności za pomocą Hilt

Hilt to biblioteka wstrzykiwania zależności na Androidzie, która ogranicza konieczność ręcznego wstrzykiwania zależności w projekcie. Użycie ręcznego wstrzykiwania zależności wymaga ręcznego tworzenia wszystkich klas i ich 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 DIT w aplikacji, udostępniając kontenery dla każdej klasy Androida w projekcie i automatycznie zarządzając ich cyklami życia. Hilt działa w oparciu o popularną bibliotekę DI Dagger, aby korzystać z poprawności w czasie kompilacji, wydajności środowiska wykonawczego, skalowalności i obsługi Android Studio zapewnianych przez Dagger. Więcej informacji znajdziesz w artykule Hilt andDagger.

W tym przewodniku opisano podstawowe pojęcia dotyczące Hilt i wygenerowanych przez nią kontenerów. Zawiera również prezentację sposobu uruchamiania istniejącej aplikacji za pomocą Hilt.

Dodawanie zależności

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

Odlotowy

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ę Gradle i dodaj te zależności w pliku app/build.gradle:

Odlotowy

...
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 {
  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 używa funkcji Java 8. Aby włączyć Java 8 w projekcie, dodaj do pliku app/build.gradle ten fragment:

Odlotowy

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 z adnotacją @HiltAndroidApp.

@HiltAndroidApp aktywuje generowanie kodu przez Hilt, w tym klasę bazową 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 dołączony do cyklu życia obiektu Application i określa jego zależności. Jest też nadrzędnym komponentem aplikacji, co oznacza, że inne komponenty mają dostęp do określonych przez nią zależności.

Wstawianie zależności do klas Androida

Gdy Hilt jest skonfigurowany w klasie Application i dostępny jest komponent na poziomie aplikacji, Hilt może wprowadzać 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 dodajesz adnotacje do klasy Androida z atrybutem @AndroidEntryPoint, musisz też dodać adnotacje do klas Androida, które są od niej zależne. Jeśli np. dodasz adnotacje do fragmentu, musisz też dodać adnotację 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 swoich klas nadrzędnych zgodnie z opisem w sekcji Hierarchia komponentów.

Aby uzyskać zależności z komponentu, wykonaj wstrzykiwanie pól za pomocą adnotacji @Inject:

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 tym, do którego wywołania zwrotnego cyklu życia jest wstrzykiwana klasa Androida, znajdziesz w sekcji Czas życia komponentów.

Zdefiniuj powiązania Hilt

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

Jednym ze sposobów przekazania informacji o wiązaniu do Hilt 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 konstruktora klasy z adnotacjami są jej zależnościami. W tym przykładzie AnalyticsAdapter ma zależność AnalyticsService. Dlatego Hilt musi też wiedzieć, jak udostępnić instancje AnalyticsService.

Moduły Hilt

Czasami nie można wstrzykiwać typu przez konstruktor. Przyczyny mogą być różne. Nie można np. tworzyć wstrzykiwania interfejsu przez konstruktor. Nie możesz też wstrzykiwać przez konstruktor typu, który nie należy do Ciebie, np. klasy z biblioteki zewnętrznej. W takich przypadkach możesz podać firmie Hilt informacje o powiązaniu, korzystając z modułów Hilt.

Moduł Hilt to klasa z adnotacjami @Module. Podobnie jak moduł Dagger, informuje on Hilt, jak udostępniać wystąpienia określonego typu. W przeciwieństwie do modułów Dagger do modułów Hilt należy dodawać adnotacje @InstallIn, aby poinformować zespół Hilt, w których klasach Androida będzie używany lub w których będzie instalowany dany moduł.

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

Wstrzyknij instancje interfejsu za pomocą @Binds

Przeanalizujmy przykład AnalyticsService. Jeśli AnalyticsService to interfejs, nie można go wstrzykiwać przez konstruktor. Zamiast tego podaj Hilt informacje o wiązaniu, tworząc w module Hilt funkcję abstrakcyjną z adnotacją @Binds.

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

Funkcja z adnotacjami przekazuje usłudze Hilt te informacje:

  • Typ zwracanej funkcji informuje Hilt interfejs, którego instancja udostępnia.
  • Parametr funkcji informuje Hilt, którą implementację 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
  );
}

W module Hilt (AnalyticsModule) znajduje się adnotacja @InstallIn(ActivityComponent.class), ponieważ chcesz, by Hilt wstrzyknęła tę zależność w elemencie ExampleActivity. Ta adnotacja oznacza, że wszystkie zależności funkcji AnalyticsModule są dostępne we wszystkich działaniach aplikacji.

Wstrzyknij instancje przy użyciu @Provides

Interfejsy to nie jedyny przypadek, w którym nie można wstrzyknąć typu przez konstruktor. Wstrzykiwanie przez konstruktora nie jest też możliwe, jeśli klasa nie należy do Ciebie, ponieważ pochodzi ona z biblioteki zewnętrznej (klasy takie jak Retrofit, OkHttpClient lub bazy danych pokoi) lub jeśli instancje muszą być tworzone za pomocą wzorca konstruktora.

Przeanalizuj poprzedni przykład. Jeśli klasa AnalyticsService nie należy bezpośrednio do Ciebie, możesz powiedzieć firmie Hilt, jak udostępniać instancje tego typu. W tym celu utwórz funkcję w module Hilt i oznacz ją adnotacją @Provides.

Funkcja z adnotacjami przekazuje do Hilt te informacje:

  • Zwracany typ funkcji informuje Hilt, jakiego typu są instancje.
  • Parametry funkcji informują Hilt zależności odpowiedniego typu.
  • Treść funkcji informuje Hilt, jak udostępnić instancję odpowiedniego typu. Hilt uruchamia treść funkcji za każdym razem, gdy musi udostępnić instancję danego 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 co zależności, musisz udostępnić Hilt wiele powiązań. Możesz zdefiniować wiele powiązań tego samego typu za pomocą kwalifikatorów.

Kwalifikator to adnotacja, która służy do identyfikowania konkretnego wiązania danego typu, gdy zdefiniowano ich wiele.

Przeanalizujmy przykład. Jeśli musisz przechwytywać wywołania AnalyticsService, możesz użyć obiektu OkHttpClient z punktem przechwytującym. W przypadku innych usług przechwytywanie połączeń może być możliwe w inny sposób. W takim przypadku musisz wskazać firmie Hilt, jak udostępnić 2 różne implementacje obiektu OkHttpClient.

Najpierw określ 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ć instancję typu odpowiadającego poszczególnym kwalifikatorom. W tym przypadku możesz użyć modułu Hilt z atrybutem @Provides. Obie metody mają ten sam typ zwrotu, ale kwalifikatory oznaczają je jako 2 różne wią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();
  }
}

Aby zastosować określony typ, dodaj do pola lub parametru odpowiednie kwalifikatory:

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 dodajesz kwalifikator do typu, dodaj kwalifikator do wszystkich możliwych sposobów ustanowienia zależności. Pozostawienie podstawowej lub typowej 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 wstępnie zdefiniowane kwalifikatory. Na przykład tak jak w przypadku klasy Context z aplikacji lub działania, Hilt udostępnia kwalifikatory @ApplicationContext i @ActivityContext.

Załóżmy, że klasa AnalyticsAdapter z przykładu wymaga kontekstu działania. Ten kod pokazuje, jak udostępnić kontekst aktywności usłudze 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 wiązaniach dostępnych w Hilt znajdziesz w sekcji Domyślne powiązania komponentów.

Wygenerowane komponenty klas Androida

Dla każdej klasy Androida, w której można wstrzykiwać pola, znajduje się powiązany komponent Hilt, o którym możesz wspomnieć w adnotacji @InstallIn. Każdy komponent Hilt odpowiada za wstrzyknięcie swoich powiązań do odpowiedniej klasy Androida.

W poprzednich przykładach pokazano wykorzystanie ActivityComponent w modułach Hilt.

Hilt zawiera te komponenty:

Komponent Ręka Irygator do
SingletonComponent Application
ActivityRetainedComponent Nie dotyczy
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View z adnotacją @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() Application zniszczono
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent Utworzono ViewModel ViewModel zniszczono
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View zniszczono
ViewWithFragmentComponent View#super() View zniszczono
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 aplikacja wysyła żądanie powiązania, Hilt tworzy nową instancję potrzebnego typu.

W tym przykładzie za każdym razem, gdy Hilt podaje AnalyticsAdapter jako zależność do innego typu lub przez wstrzykiwanie pól (jak w ExampleActivity), Hilt udostępnia nową instancję AnalyticsAdapter.

Hilt umożliwia jednak także ograniczenie powiązania do konkretnego komponentu. Hilt tworzy powiązanie zakresu tylko raz na instancję komponentu, do którego ma zastosowanie, a wszystkie żądania tego powiązania korzystają z tej samej instancji.

W tabeli poniżej znajdziesz 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 z adnotacją @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

W tym przykładzie, jeśli ustawisz zakres AnalyticsAdapter na ActivityComponent za pomocą @ActivityScoped, Hilt będzie udostępniać tę samą instancję AnalyticsAdapter przez cały okres trwania odpowiadającej mu 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, aby ta sama instancja była używana za każdym razem – nie tylko w ExampleActivity, ale w dowolnym miejscu w aplikacji. W takim przypadku można przypisać zakres AnalyticsService do SingletonComponent. W efekcie za każdym razem, gdy komponent musi udostępnić wystąpienie obiektu AnalyticsService, za każdym razem udostępnia tę samą instancję.

Poniższy przykład pokazuje, jak określić zakres powiązania z komponentem w module Hilt. Zakres powiązania musi być zgodny z zakresem komponentu, w którym został zainstalowany, więc w tym przykładzie musisz zainstalować tag AnalyticsService w SingletonComponent 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 zakresach komponentów Hilt znajdziesz w artykule Określanie zakresu w Androidzie i Hilt.

Hierarchia komponentów

Zainstalowanie modułu w komponencie umożliwia dostęp do jego powiązań jako zależność od innych powiązań tego komponentu lub dowolnego komponentu podrzędnego w hierarchii komponentów:

Element ViewWithFragmentKomponent znajduje się w sekcji FragmentKomponent. Fragmenty FragmentKomponent i ViewKomponent znajdują się w sekcji ActivityKomponent. ActivityKomponent znajduje się w sekcji ActivityRetainedKomponent. Element ViewModelKomponent znajduje się w sekcji ActivityRetainedKomponent. ActivityRetainedKomponent i ServiceKomponent znajdują się w ramach SingletonKomponent.
Rysunek 1. Hierarchia komponentów generowanych przez Hilt.

Domyślne powiązania komponentów

Każdy komponent Hilt zawiera zestaw domyślnych wiązań, które Hilt może wstrzykiwać jako zależności do Twoich niestandardowych powiązań. Te powiązania odpowiadają ogólnym typom aktywności i fragmentów, a nie żadnej konkretnej podklasie. Dzieje się tak, ponieważ Hilt używa definicji jednego komponentu aktywności do wstrzykiwania wszystkich aktywności. Każda aktywność ma inne wystąpienie 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 w @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 w @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;
  }
}

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

Hilt obsługuje najpopularniejsze zajęcia na Androidzie. Może być jednak konieczne wykonanie wstrzykiwania 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, który jest zarządzany przez Hilt, a kodem, który nim nie jest. To w miejscu, w którym kod po raz pierwszy trafia na wykres obiektów, którymi zarządza Hilt. Punkty wejścia pozwalają Hilt używać kodu, którego Hilt nie zarządza do generowania zależności w grafie zależności.

Na przykład Hilt nie wspiera bezpośrednio dostawców treści. Jeśli chcesz, aby dostawca treści wykorzystywał Hilt do pobierania zależności, musisz zdefiniować interfejs z adnotacją @EntryPoint dla każdego typu wiązania, którego chcesz używać, i uwzględnić kwalifikatory. Następnie dodaj @InstallIn, aby określić komponent, w którym chcesz zainstalować punkt wejścia, w ten sposób:

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 działającym jako właściciel 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 do pobrania punktu wejścia należy użyć ApplicationContext, ponieważ punkt wejścia jest zainstalowany w środowisku SingletonComponent. Jeśli powiązanie, które chcesz pobrać, znajduje się w elemencie ActivityComponent, zamiast tego użyj elementu ActivityContext.

Ręka i sztylet

Hilt działa na bazie biblioteki wstrzykiwania zależności Dagger, co pozwala w standardowy sposób stosować Dagger w aplikacji na Androida.

W odniesieniu do Daggera celem Hilt jest:

  • Aby uprościć infrastrukturę związaną z Daggerem dla aplikacji na Androida.
  • Aby utworzyć standardowy zestaw komponentów i zakresów, aby 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 testowanie, debugowanie lub wersja.

System operacyjny Android tworzy wiele własnych klas platformy, dlatego użycie Dagger w aplikacji na Androida wymaga napisania znacznej ilości stałych elementów. Hilt skraca powtarzalny kod, który pojawia się przy korzystaniu ze Daggera w aplikacji na Androida. Hilt automatycznie generuje i udostępnia:

  • Komponenty do integrowania klas platformy Android z Daggerem, które w innym przypadku należałoby tworzyć ręcznie.
  • Adnotacje zakresu używane 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 i @ActivityContext.

Kody Dagger i 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 Dagger do Hilt, zapoznaj się z przewodnikiem po migracji i ćwiczeniem z programowania dotyczącym migracji aplikacji Dagger do Hilt.

Dodatkowe materiały

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

Próbki

Ćwiczenia z programowania

Blogi