Abhängigkeitsinjektion mit Hilt

Hilt ist eine Bibliothek für die Abhängigkeitsinjektion für Android, die den Boilerplate-Code für die manuelle Abhängigkeitsinjektion in Ihrem Projekt reduziert. Bei der manuellen Abhängigkeitsinjektion müssen Sie jede Klasse und ihre Abhängigkeiten manuell erstellen und Container verwenden, um Abhängigkeiten wiederzuverwenden und zu verwalten.

Hilt bietet eine standardisierte Möglichkeit, DI in Ihrer Anwendung zu verwenden. Dazu werden Container für jede Android-Klasse in Ihrem Projekt bereitgestellt und deren Lebenszyklen automatisch verwaltet. Hilt basiert auf der beliebten DI-Bibliothek Dagger und profitiert von der Korrektheit zur Kompilierzeit, der Laufzeitleistung, der Skalierbarkeit und der Android Studio-Unterstützung, die Dagger bietet. Weitere Informationen finden Sie unter Hilt und Dagger.

In diesem Leitfaden werden die grundlegenden Konzepte von Hilt und den generierten Containern erläutert. Außerdem wird gezeigt, wie eine vorhandene App für die Verwendung von Hilt vorbereitet wird.

Abhängigkeiten hinzufügen

Fügen Sie zuerst das hilt-android-gradle-plugin-Plug-in in die build.gradle-Datei des Stammverzeichnisses Ihres Projekts ein:

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
}

Wenden Sie dann das Gradle-Plug-in an und fügen Sie diese Abhängigkeiten in der Datei app/build.gradle hinzu:

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 verwendet Java 8-Funktionen. Wenn Sie Java 8 in Ihrem Projekt aktivieren möchten, fügen Sie der Datei app/build.gradle Folgendes hinzu:

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

Hilt-Anwendungsklasse

Alle Apps, die Hilt verwenden, müssen eine Application-Klasse enthalten, die mit @HiltAndroidApp annotiert ist.

@HiltAndroidApp löst die Codegenerierung von Hilt aus, einschließlich einer Basisklasse für Ihre Anwendung, die als Abhängigkeitscontainer auf Anwendungsebene dient.

Kotlin

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

Java

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

Diese generierte Hilt-Komponente ist an den Lebenszyklus des Application-Objekts angehängt und stellt Abhängigkeiten für dieses Objekt bereit. Außerdem ist sie die übergeordnete Komponente der App. Das bedeutet, dass andere Komponenten auf die von ihr bereitgestellten Abhängigkeiten zugreifen können.

Abhängigkeiten in Android-Klassen einfügen

Sobald Hilt in Ihrer Application-Klasse eingerichtet und eine Komponente auf Anwendungsebene verfügbar ist, kann Hilt Abhängigkeiten für andere Android-Klassen mit der Annotation @AndroidEntryPoint bereitstellen:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity { ... }

Hilt unterstützt derzeit die folgenden Android-Klassen:

  • Application (mit @HiltAndroidApp)
  • ViewModel (mit @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Wenn Sie eine Android-Klasse mit @AndroidEntryPoint annotieren, müssen Sie auch Android-Klassen annotieren, die von ihr abhängen. Wenn Sie beispielsweise ein Fragment mit einer Anmerkung versehen, müssen Sie auch alle Aktivitäten mit einer Anmerkung versehen, in denen Sie dieses Fragment verwenden.

@AndroidEntryPoint generiert für jede Android-Klasse in Ihrem Projekt eine individuelle Hilt-Komponente. Diese Komponenten können Abhängigkeiten von ihren jeweiligen übergeordneten Klassen empfangen, wie in der Komponentenhierarchie beschrieben.

Verwenden Sie die Annotation @Inject, um Abhängigkeiten aus einer Komponente abzurufen und die Feldinjektion durchzuführen:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Klassen, die von Hilt eingefügt werden, können andere Basisklassen haben, die ebenfalls die Einfügung verwenden. Für diese Klassen ist die Annotation @AndroidEntryPoint nicht erforderlich, wenn sie abstrakt sind.

Weitere Informationen dazu, in welchen Lebenszyklus-Callback eine Android-Klasse eingefügt wird, finden Sie unter Component lifetimes.

Hilt-Bindungen definieren

Für die Feldinjektion muss Hilt wissen, wie Instanzen der erforderlichen Abhängigkeiten aus der entsprechenden Komponente bereitgestellt werden. Eine Bindung enthält die Informationen, die erforderlich sind, um Instanzen eines Typs als Abhängigkeit bereitzustellen.

Eine Möglichkeit, Hilt Bindungsinformationen bereitzustellen, ist die Konstruktorinjektion. Verwenden Sie die Annotation @Inject für den Konstruktor einer Klasse, um Hilt mitzuteilen, wie Instanzen dieser Klasse bereitgestellt werden sollen:

Kotlin

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }

Java

public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

Die Parameter eines mit Anmerkungen versehenen Konstruktors einer Klasse sind die Abhängigkeiten dieser Klasse. Im Beispiel hat AnalyticsAdapter die Abhängigkeit AnalyticsService. Daher muss Hilt auch wissen, wie Instanzen von AnalyticsService bereitgestellt werden.

Hilt-Module

Manchmal kann ein Typ nicht per Constructor Injection eingefügt werden. Dafür kann es verschiedene Gründe geben. Sie können beispielsweise keine Schnittstelle per Konstruktor-Injection einfügen. Sie können auch keinen Typ per Konstruktor-Injection einfügen, der Ihnen nicht gehört, z. B. eine Klasse aus einer externen Bibliothek. In diesen Fällen können Sie Hilt mithilfe von Hilt-Modulen Bindungsinformationen bereitstellen.

Ein Hilt-Modul ist eine Klasse, die mit @Module annotiert ist. Wie ein Dagger-Modul informiert es Hilt darüber, wie Instanzen bestimmter Typen bereitgestellt werden. Im Gegensatz zu Dagger-Modulen müssen Sie Hilt-Module mit @InstallIn annotieren, um Hilt mitzuteilen, in welcher Android-Klasse jedes Modul verwendet oder installiert wird.

Abhängigkeiten, die Sie in Hilt-Modulen bereitstellen, sind in allen generierten Komponenten verfügbar, die der Android-Klasse zugeordnet sind, in der Sie das Hilt-Modul installieren.

Schnittstelleninstanzen mit @Binds einfügen

Sehen Sie sich das AnalyticsService-Beispiel an. Wenn AnalyticsService eine Schnittstelle ist, können Sie sie nicht per Konstruktor einfügen. Stattdessen müssen Sie Hilt die Bindungsinformationen bereitstellen, indem Sie in einem Hilt-Modul eine abstrakte Funktion mit der Annotation @Binds erstellen.

Die Annotation @Binds weist Hilt an, welche Implementierung verwendet werden soll, wenn eine Instanz einer Schnittstelle bereitgestellt werden muss.

Die annotierte Funktion stellt Hilt die folgenden Informationen zur Verfügung:

  • Der Rückgabetyp der Funktion gibt an, für welche Schnittstelle die Funktion Instanzen bereitstellt.
  • Der Funktionsparameter gibt an, welche Implementierung Hilt bereitstellen soll.

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

Das Hilt-Modul AnalyticsModule ist mit @InstallIn(ActivityComponent.class) annotiert, weil Sie möchten, dass Hilt diese Abhängigkeit in ExampleActivity einfügt. Diese Anmerkung bedeutet, dass alle Abhängigkeiten in AnalyticsModule in allen Aktivitäten der App verfügbar sind.

Instanzen mit @Provides einfügen

Schnittstellen sind nicht der einzige Fall, in dem Sie einen Typ nicht per Konstruktor einfügen können. Die Konstruktor-Injection ist auch nicht möglich, wenn Sie die Klasse nicht besitzen, weil sie aus einer externen Bibliothek stammt (Klassen wie Retrofit, OkHttpClient oder Room-Datenbanken) oder wenn Instanzen mit dem Builder-Muster erstellt werden müssen.

Sehen Sie sich das vorherige Beispiel an. Wenn Sie die Klasse AnalyticsService nicht direkt besitzen, können Sie Hilt mitteilen, wie Instanzen dieses Typs bereitgestellt werden sollen. Erstellen Sie dazu eine Funktion in einem Hilt-Modul und versehen Sie diese Funktion mit der Annotation @Provides.

Die annotierte Funktion liefert die folgenden Informationen an Hilt:

  • Der Rückgabetyp der Funktion gibt an, von welchem Typ die Funktion Instanzen bereitstellt.
  • Die Funktionsparameter geben Hilt die Abhängigkeiten des entsprechenden Typs an.
  • Der Funktionsrumpf gibt Hilt an, wie eine Instanz des entsprechenden Typs bereitgestellt werden soll. Hilt führt den Funktionskörper jedes Mal aus, wenn eine Instanz dieses Typs bereitgestellt werden muss.

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

Mehrere Bindungen für denselben Typ bereitstellen

Wenn Hilt verschiedene Implementierungen desselben Typs als Abhängigkeiten bereitstellen soll, müssen Sie Hilt mehrere Bindungen zur Verfügung stellen. Sie können mit Qualifizierern mehrere Bindungen für denselben Typ definieren.

Ein Qualifier ist eine Annotation, mit der Sie eine bestimmte Bindung für einen Typ identifizieren, wenn für diesen Typ mehrere Bindungen definiert sind.

Sehen Sie sich das Beispiel an. Wenn Sie Aufrufe von AnalyticsService abfangen müssen, können Sie ein OkHttpClient-Objekt mit einem Interceptor verwenden. Bei anderen Diensten müssen Sie Anrufe möglicherweise auf andere Weise abfangen. In diesem Fall müssen Sie Hilt mitteilen, wie zwei verschiedene Implementierungen von OkHttpClient bereitgestellt werden sollen.

Definieren Sie zuerst die Qualifizierer, die Sie zum Annotieren der Methoden @Binds oder @Provides verwenden:

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

Als Nächstes muss Hilt wissen, wie eine Instanz des Typs bereitgestellt wird, der mit jedem Qualifier übereinstimmt. In diesem Fall können Sie ein Hilt-Modul mit @Provides verwenden. Beide Methoden haben denselben Rückgabetyp, aber die Qualifizierer kennzeichnen sie als zwei unterschiedliche Bindungen:

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();
  }
}

Sie können den benötigten Typ einfügen, indem Sie das Feld oder den Parameter mit dem entsprechenden Qualifier annotieren:

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

Wenn Sie einem Typ einen Qualifier hinzufügen, sollten Sie als Best Practice Qualifier für alle möglichen Arten hinzufügen, diese Abhängigkeit bereitzustellen. Wenn Sie die Basis- oder gemeinsame Implementierung ohne Qualifier lassen, ist das fehleranfällig und kann dazu führen, dass Hilt die falsche Abhängigkeit einfügt.

Vordefinierte Qualifizierer in Hilt

Hilt bietet einige vordefinierte Qualifizierer. Da Sie beispielsweise die Klasse Context möglicherweise sowohl von der Anwendung als auch von der Aktivität benötigen, bietet Hilt die Qualifizierer @ApplicationContext und @ActivityContext.

Angenommen, die Klasse AnalyticsAdapter aus dem Beispiel benötigt den Kontext der Aktivität. Der folgende Code zeigt, wie Sie den Aktivitätskontext für AnalyticsAdapter bereitstellen:

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

Informationen zu anderen vordefinierten Bindungen, die in Hilt verfügbar sind, finden Sie unter Standardbindungen für Komponenten.

Generierte Komponenten für Android-Klassen

Für jede Android-Klasse, in der Sie die Feldinjektion durchführen können, gibt es eine zugehörige Hilt-Komponente, auf die Sie in der Annotation @InstallIn verweisen können. Jede Hilt-Komponente ist dafür verantwortlich, ihre Bindungen in die entsprechende Android-Klasse einzufügen.

In den vorherigen Beispielen wurde die Verwendung von ActivityComponent in Hilt-Modulen veranschaulicht.

Hilt bietet die folgenden Komponenten:

Hilt-Komponente Injektor für
SingletonComponent Application
ActivityRetainedComponent
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View mit @WithFragmentBindings annotiert
ServiceComponent Service

Lebensdauer von Komponenten

Hilt erstellt und zerstört automatisch Instanzen generierter Komponentenklassen entsprechend dem Lebenszyklus der entsprechenden Android-Klassen.

Generierte Komponente Erstellt um Zerstört am
SingletonComponent Application#onCreate() Application zerstört
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel erstellt ViewModel zerstört
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View zerstört
ViewWithFragmentComponent View#super() View zerstört
ServiceComponent Service#onCreate() Service#onDestroy()

Komponentenbereiche

Standardmäßig sind alle Bindungen in Hilt unscoped. Das bedeutet, dass Hilt jedes Mal, wenn Ihre App die Bindung anfordert, eine neue Instanz des benötigten Typs erstellt.

Im Beispiel stellt Hilt jedes Mal, wenn AnalyticsAdapter als Abhängigkeit für einen anderen Typ oder durch die Feldinjektion (wie in ExampleActivity) bereitgestellt wird, eine neue Instanz von AnalyticsAdapter bereit.

Mit Hilt kann eine Bindung jedoch auch auf eine bestimmte Komponente beschränkt werden. Hilt erstellt ein bereichsbezogenes Binding nur einmal pro Instanz der Komponente, auf die das Binding beschränkt ist. Alle Anfragen für dieses Binding verwenden dieselbe Instanz.

In der folgenden Tabelle sind Bereichsanmerkungen für jede generierte Komponente aufgeführt:

Android-Klasse Generierte Komponente Umfang
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View mit @WithFragmentBindings annotiert ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

Wenn Sie im Beispiel AnalyticsAdapter mit @ActivityScoped auf ActivityComponent beschränken, stellt Hilt während des gesamten Lebenszyklus der entsprechenden Aktivität dieselbe Instanz von AnalyticsAdapter bereit:

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

Angenommen, AnalyticsService hat einen internen Status, für den jedes Mal dieselbe Instanz verwendet werden muss – nicht nur in ExampleActivity, sondern überall in der App. In diesem Fall ist es sinnvoll, AnalyticsService auf SingletonComponent zu beschränken. Das Ergebnis ist, dass die Komponente immer dieselbe Instanz bereitstellt, wenn sie eine Instanz von AnalyticsService bereitstellen muss.

Das folgende Beispiel zeigt, wie Sie eine Bindung auf eine Komponente in einem Hilt-Modul beschränken. Der Bereich einer Bindung muss mit dem Bereich der Komponente übereinstimmen, in der sie installiert ist. In diesem Beispiel müssen Sie AnalyticsService also in SingletonComponent anstelle von ActivityComponent installieren:

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

Weitere Informationen zu Hilt-Komponentenbereichen finden Sie unter Bereich in Android und Hilt.

Komponentenhierarchie

Wenn Sie ein Modul in einer Komponente installieren, kann auf seine Bindungen als Abhängigkeit anderer Bindungen in dieser Komponente oder in einer untergeordneten Komponente in der Komponentenstruktur zugegriffen werden:

„ViewWithFragmentComponent“ befindet sich unter „FragmentComponent“. FragmentComponent und ViewComponent befinden sich unter ActivityComponent. ActivityComponent befindet sich unter ActivityRetainedComponent. ViewModelComponent befindet sich unter ActivityRetainedComponent. ActivityRetainedComponent und ServiceComponent befinden sich unter SingletonComponent.
Abbildung 1: Hierarchie der von Hilt generierten Komponenten.

Standardbindungen für Komponenten

Jede Hilt-Komponente enthält eine Reihe von Standardbindungen, die Hilt als Abhängigkeiten in Ihre eigenen benutzerdefinierten Bindungen einfügen kann. Diese Bindungen entsprechen den allgemeinen Aktivitäts- und Fragmenttypen und nicht einer bestimmten Unterklasse. Das liegt daran, dass Hilt eine einzelne Aktivitätskomponentendefinition verwendet, um alle Aktivitäten einzufügen. Jede Aktivität hat eine andere Instanz dieser Komponente.

Android-Komponente Standardbindungen
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

Die Bindung des Anwendungskontexts ist auch über @ApplicationContext verfügbar. Beispiel:

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

Die Bindung des Aktivitätskontexts ist auch über @ActivityContext verfügbar. Beispiel:

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

Abhängigkeiten in Klassen einfügen, die nicht von Hilt unterstützt werden

Hilt unterstützt die gängigsten Android-Klassen. Möglicherweise müssen Sie jedoch die Feldinjektion in Klassen durchführen, die von Hilt nicht unterstützt werden.

In diesen Fällen können Sie mit der Annotation @EntryPoint einen Einstiegspunkt erstellen. Ein Einstiegspunkt ist die Grenze zwischen Code, der von Hilt verwaltet wird, und Code, der nicht von Hilt verwaltet wird. Das ist der Punkt, an dem Code zum ersten Mal in den von Hilt verwalteten Objektgraphen eintritt. Einstiegspunkte ermöglichen es Hilt, Code zu verwenden, der nicht von Hilt verwaltet wird, um Abhängigkeiten im Abhängigkeitsdiagramm bereitzustellen.

Hilt unterstützt beispielsweise keine Content-Provider. Wenn ein Content-Anbieter Hilt verwenden soll, um einige Abhängigkeiten abzurufen, müssen Sie für jeden gewünschten Bindungstyp eine mit @EntryPoint annotierte Schnittstelle definieren und Qualifizierer einfügen. Fügen Sie dann @InstallIn hinzu, um die Komponente anzugeben, in der der Einstiegspunkt installiert werden soll:

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

Verwenden Sie zum Zugriff auf einen Einstiegspunkt die entsprechende statische Methode aus EntryPointAccessors. Der Parameter sollte entweder die Komponenteninstanz oder das @AndroidEntryPoint-Objekt sein, das als Komponenten-Container fungiert. Achten Sie darauf, dass die Komponente, die Sie als Parameter übergeben, und die statische Methode EntryPointAccessors beide mit der Android-Klasse in der Annotation @InstallIn auf der Schnittstelle @EntryPoint übereinstimmen:

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();
  }
}

In diesem Beispiel müssen Sie ApplicationContext verwenden, um den Einstiegspunkt abzurufen, da er in SingletonComponent installiert ist. Wenn sich die Bindung, die Sie abrufen möchten, in der ActivityComponent befindet, verwenden Sie stattdessen die ActivityContext.

Hilt and Dagger

Hilt basiert auf der Dagger-Bibliothek für die Abhängigkeitsinjektion und bietet eine Standardmethode zum Einbinden von Dagger in eine Android-Anwendung.

Die Ziele von Hilt in Bezug auf Dagger sind:

  • Um die Dagger-bezogene Infrastruktur für Android-Apps zu vereinfachen.
  • Erstellen eines Standardsatzes von Komponenten und Bereichen, um die Einrichtung, Lesbarkeit und gemeinsame Nutzung von Code zwischen Apps zu vereinfachen.
  • Um eine einfache Möglichkeit zu bieten, verschiedene Bindungen für verschiedene Build-Typen wie Test-, Debug- oder Release-Builds bereitzustellen.

Da das Android-Betriebssystem viele eigene Framework-Klassen instanziiert, müssen Sie bei der Verwendung von Dagger in einer Android-App viel Boilerplate-Code schreiben. Hilt reduziert den Boilerplate-Code, der für die Verwendung von Dagger in einer Android-Anwendung erforderlich ist. Hilt generiert und stellt automatisch Folgendes bereit:

  • Komponenten zum Einbinden von Android-Framework-Klassen in Dagger, die Sie sonst manuell erstellen müssten.
  • Umfangsanmerkungen für die Verwendung mit den Komponenten, die Hilt automatisch generiert.
  • Vordefinierte Bindungen zur Darstellung von Android-Klassen wie Application oder Activity.
  • Vordefinierte Qualifizierer für @ApplicationContext und @ActivityContext.

Dagger- und Hilt-Code können in derselben Codebasis vorhanden sein. In den meisten Fällen ist es jedoch am besten, Hilt zu verwenden, um die gesamte Verwendung von Dagger unter Android zu verwalten. Wenn Sie ein Projekt, in dem Dagger verwendet wird, zu Hilt migrieren möchten, lesen Sie den Migrationsleitfaden und das Codelab zur Migration Ihrer Dagger-App zu Hilt.

Zusätzliche Ressourcen

Weitere Informationen zu Hilt finden Sie in den folgenden zusätzlichen Ressourcen.

Produktproben

Codelabs

Blogs