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:
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
lubActivity
. - 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
- Wstrzykiwanie zależności na Androidzie za pomocą Hilta
- Ograniczanie zakresu w Androidzie i Hilt
- Dodawanie komponentów do hierarchii Hilt
- Migracja aplikacji Google I/O do Hilt