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 OkHttpClient
z interceptor. 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
i @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ć AnalyticsService
w SingletonComponent
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:
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
lubActivity
. - Wstępnie zdefiniowane kwalifikatory reprezentujące
@ApplicationContext
i@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 migracji i samouczkiem Migrowanie aplikacji Dagger na Hilt.
Dodatkowe materiały
Więcej informacji o Hilt znajdziesz w tych dodatkowych materiałach.
Próbki
Codelabs
Blogi
- Wstrzykiwanie zależności na Androidzie za pomocą Hilt
- Określanie zakresu w Androidzie i Hilt
- Dodawanie komponentów do hierarchii Hilt
- Migracja aplikacji Google I/O do Hilta