Abhängigkeitsinjektion mit Hilt

Hilt ist eine Abhängigkeitsinjektionsbibliothek für Android, die den Standardcode 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 Standardmethode zur Verwendung von DI in Ihrer Anwendung. Dazu stellt es Container für jede Android-Klasse in Ihrem Projekt bereit und verwaltet deren Lebenszyklen automatisch. Hilt baut auf der beliebten DI-Bibliothek Dagger auf, um von der Kompilierungszeit, der Laufzeitleistung, Skalierbarkeit und der Android Studio-Unterstützung zu profitieren, die Dagger bietet. Weitere Informationen finden Sie unter Hilt undDagger.

In diesem Leitfaden werden die grundlegenden Konzepte von Hilt und die generierten Container erläutert. Außerdem wird gezeigt, wie Sie einer vorhandenen App Hilt per Bootstrapping zur Verfügung stellen.

Abhängigkeiten hinzufügen

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

Groovig

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
}

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

Groovig

...
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 verwendet Java 8-Funktionen. Fügen Sie der Datei app/build.gradle Folgendes hinzu, um Java 8 in Ihrem Projekt zu aktivieren:

Groovig

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 Anwendungen, 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 bereit. Außerdem ist sie die übergeordnete Komponente der Anwendung. Das bedeutet, dass andere Komponenten auf die von ihr bereitgestellten Abhängigkeiten zugreifen können.

Abhängigkeiten in Android-Klassen einschleusen

Sobald Hilt in Ihrer Application-Klasse eingerichtet ist 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 (durch Verwendung von @HiltAndroidApp)
  • ViewModel (durch Verwendung von @HiltViewModel)
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

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

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

Wenn Sie Abhängigkeiten von einer Komponente abrufen möchten, verwenden Sie die Annotation @Inject, um eine Feldeinschleusung 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 injiziert werden, können andere Basisklassen haben, die ebenfalls die Injektion verwenden. Diese Klassen benötigen die Annotation @AndroidEntryPoint nicht, wenn sie abstrakt sind.

Weitere Informationen dazu, in welchen Lebenszyklus-Callback eine Android-Klasse eingeschleust wird, finden Sie unter Komponentenlebensdauer.

Hilt-Bindungen definieren

Um eine Feldeinschleusung durchzuführen, muss Hilt wissen, wie Instanzen der erforderlichen Abhängigkeiten 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, Bindungsinformationen an Hilt bereitzustellen, ist die Konstruktor-Injektion. Mit der Annotation @Inject für den Konstruktor einer Klasse teilen Sie Hilt mit, 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 annotierten Konstruktors einer Klasse sind die Abhängigkeiten dieser Klasse. Im Beispiel hat AnalyticsAdapter die Abhängigkeit von AnalyticsService. Daher muss Hilt auch wissen, wie Instanzen von AnalyticsService bereitgestellt werden.

Griffmodule

Manchmal kann ein Typ nicht durch den Konstruktor eingeschleust werden. Dafür kann es mehrere Gründe geben. Sie können beispielsweise keine Schnittstelle einschleusen. Sie können auch keine Konstruktor-Injektion eines Typs verwenden, der Ihnen nicht gehört, z. B. eine Klasse aus einer externen Bibliothek. In diesen Fällen können Sie Hilt Bindungsinformationen mithilfe von Hilt-Modulen zur Verfügung stellen.

Ein Hilt-Modul ist eine Klasse, die mit @Module annotiert ist. Wie bei einem Dagger-Modul gibt es Hilt an, 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 die einzelnen Module verwendet oder installiert werden.

Abhängigkeiten, die Sie in Hilt-Modulen angeben, 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 wir uns das Beispiel AnalyticsService an. Wenn AnalyticsService eine Schnittstelle ist, können Sie sie nicht durch den Konstruktor injizieren. Stellen Sie stattdessen Hilt die Bindungsinformationen bereit. Erstellen Sie dazu eine abstrakte Funktion, die in einem Hilt-Modul mit @Binds annotiert ist.

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

Die annotierte Funktion stellt Hilt die folgenden Informationen bereit:

  • Der Rückgabetyp der Funktion teilt Hilt mit, von welcher Schnittstelle die Funktion Instanzen bereitstellt.
  • Der Funktionsparameter teilt Hilt mit, welche Implementierung bereitgestellt werden 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, da Hilt diese Abhängigkeit in ExampleActivity einschleusen soll. Diese Annotation bedeutet, dass alle Abhängigkeiten in AnalyticsModule in allen Aktivitäten der Anwendung verfügbar sind.

Instanzen mit @Provides einfügen

Schnittstellen sind nicht der einzige Fall, in dem es nicht möglich ist, einen Typ durch den Konstruktor einzuschleusen. Eine Konstruktoreinschleusung ist auch nicht möglich, wenn Sie nicht Inhaber der Klasse sind, da sie aus einer externen Bibliothek stammt (Klassen wie Retrofit, OkHttpClient oder Raumdatenbanken) oder wenn Instanzen mit dem Builder-Muster erstellt werden müssen.

Betrachten Sie das vorherige Beispiel. Wenn Sie nicht direkt Inhaber der AnalyticsService-Klasse sind, können Sie Hilt anweisen, wie Instanzen dieses Typs bereitgestellt werden sollen. Dazu erstellen Sie eine Funktion in einem Hilt-Modul und annotieren diese Funktion mit @Provides.

Die annotierte Funktion liefert die folgenden Informationen an Hilt:

  • Der Rückgabetyp der Funktion teilt Hilt mit, von welchem Typ die Funktion Instanzen bereitstellt.
  • Die Funktionsparameter informieren Hilt über die Abhängigkeiten des entsprechenden Typs.
  • Der Funktionstext teilt Hilt mit, wie eine Instanz des entsprechenden Typs bereitgestellt wird. Hilt führt den Funktionstext 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 angeben

In Fällen, in denen Hilt verschiedene Implementierungen desselben Typs als Abhängigkeiten bereitstellen soll, müssen Sie mehrere Bindungen bereitstellen. Mit Qualifizierern können Sie 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.

Betrachten Sie das Beispiel. Wenn Sie Aufrufe an 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 {}

Dann muss Hilt wissen, wie eine Instanz des Typs bereitgestellt wird, der jedem Qualifier entspricht. In diesem Fall könnten Sie ein Hilt-Modul mit @Provides verwenden. Beide Methoden haben denselben Rückgabetyp, aber die Qualifier kennzeichnen sie als zwei verschiedene 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 von Ihnen benötigten Typ injizieren, 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;
  ...
}

Als Best Practice gilt: Wenn Sie einem Typ einen Qualifier hinzufügen, sollten Sie allen möglichen Möglichkeiten zum Bereitstellen dieser Abhängigkeit Qualifier hinzufügen. Wenn Sie die Basis- oder gängige Implementierung ohne Qualifier lassen, ist dies fehleranfällig und kann dazu führen, dass Hilt die falsche Abhängigkeit einfügt.

Vordefinierte Qualifier in Hilt

Hilt bietet einige vordefinierte Qualifier. Wenn Sie beispielsweise die Klasse Context aus der Anwendung oder der Aktivität benötigen, stellt Hilt die Qualifier @ApplicationContext und @ActivityContext bereit.

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

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

Weitere vordefinierte Bindungen, die in Hilt verfügbar sind, finden Sie unter Standardbindungen für Komponenten.

Generierte Komponenten für Android-Klassen

Jeder Android-Klasse, in der Sie eine Feldeinschleusung 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 einzuschleusen.

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

Hilt bietet die folgenden Komponenten:

Griffkomponente Injektor für
SingletonComponent Application
ActivityRetainedComponent
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View mit @WithFragmentBindings kommentiert
ServiceComponent Service

Komponentenlebensdauer

Hilt erstellt und löscht automatisch Instanzen generierter Komponentenklassen nach dem Lebenszyklus der entsprechenden Android-Klassen.

Generierte Komponente Erstellt um Gelöscht um
SingletonComponent Application#onCreate() Application gelöscht
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel erstellt ViewModel gelöscht
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View gelöscht
ViewWithFragmentComponent View#super() View gelöscht
ServiceComponent Service#onCreate() Service#onDestroy()

Komponentenbereiche

Standardmäßig haben alle Bindungen in Hilt keinen Geltungsbereich. Das bedeutet, dass Hilt jedes Mal, wenn Ihre Anwendung die Bindung anfordert, eine neue Instanz des erforderlichen Typs erstellt.

In diesem Beispiel stellt Hilt jedes Mal, wenn AnalyticsAdapter als Abhängigkeit zu einem anderen Typ oder durch Feldinjektion (wie in ExampleActivity) angegeben wurde, eine neue Instanz von AnalyticsAdapter bereit.

Mit Hilt kann eine Bindung jedoch auch auf eine bestimmte Komponente beschränkt werden. Mit Hilt wird eine bereichsspezifische Bindung nur einmal pro Instanz der Komponente erstellt, der die Bindung zugeordnet ist. Alle Anfragen für diese Bindung verwenden dieselbe Instanz.

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

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

Wenn Sie in diesem Beispiel AnalyticsAdapter mithilfe von @ActivityScoped auf ActivityComponent festlegen, stellt Hilt während der gesamten Lebensdauer 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, bei dem jedes Mal dieselbe Instanz verwendet werden muss – nicht nur in ExampleActivity, sondern an beliebiger Stelle in der Anwendung. In diesem Fall ist es angemessen, AnalyticsService auf SingletonComponent zu beschränken. Das hat zur Folge, dass jedes Mal, wenn die Komponente eine Instanz von AnalyticsService angeben muss, dieselbe Instanz bereitgestellt wird.

Im folgenden Beispiel wird gezeigt, wie Sie eine Bindung auf eine Komponente in einem Zugriffsmodul festlegen. Der Bereich einer Bindung muss mit dem Bereich der Komponente übereinstimmen, in der sie installiert ist. Daher müssen Sie in diesem Beispiel AnalyticsService in SingletonComponent statt in 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 Umfang in Android und Hilt.

Komponentenhierarchie

Wenn ein Modul in einer Komponente installiert wird, kann der Zugriff auf seine Bindungen in Abhängigkeit von anderen Bindungen in dieser Komponente oder in jeder untergeordneten Komponente in der Komponentenhierarchie erfolgen:

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

Standardbindungen von Komponenten

Jede Hilt-Komponente enthält eine Reihe von Standardbindungen, die Hilt als Abhängigkeiten in Ihre eigenen benutzerdefinierten Bindungen einschleusen 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 einzuschleusen. Jede Aktivität hat eine andere Instanz dieser Komponente.

Android-Komponente Standardbindungen
SingletonComponent Application
ActivityRetainedComponent Application
ViewModelComponent SavedStateHandle
ActivityComponent Application, Activity
FragmentComponent Application, Activity und Fragment
ViewComponent Application, Activity und View
ViewWithFragmentComponent Application, Activity, Fragment, View
ServiceComponent Application, Service

Die Kontextbindung der Anwendung ist auch mit @ApplicationContext verfügbar. Beispiele:

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 Aktivitätskontextbindung ist auch mit @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 einschleusen, die nicht von Hilt unterstützt werden

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

In diesen Fällen können Sie einen Einstiegspunkt mit der Annotation @EntryPoint erstellen. Ein Einstiegspunkt ist die Grenze zwischen Code, der von Hilt verwaltet wird, und Code, der nicht von Hilt verwaltet wird. An diesem Punkt gelangt Code zum ersten Mal in das Diagramm der Objekte, die von Hilt verwaltet werden. Einstiegspunkte ermöglichen es Hilt, Code zu verwenden, den Hilt nicht verwaltet, um Abhängigkeiten innerhalb des Abhängigkeitsdiagramms bereitzustellen.

Hilt unterstützt beispielsweise Contentanbieter nicht direkt. Wenn ein Contentanbieter Hilt zum Abrufen einiger Abhängigkeiten verwenden soll, müssen Sie eine Schnittstelle definieren, die für jeden gewünschten Bindungstyp mit @EntryPoint annotiert ist und Qualifizierer einschließt. 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 die entsprechende statische Methode aus EntryPointAccessors, um auf einen Einstiegspunkt zuzugreifen. Der Parameter sollte entweder die Komponenteninstanz oder das @AndroidEntryPoint-Objekt sein, das als Komponenteninhaber dient. Achten Sie darauf, dass die als Parameter übergebene Komponente und die statische Methode EntryPointAccessors der Android-Klasse in der Annotation @InstallIn auf der @EntryPoint-Schnittstelle entsprechen:

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 den ApplicationContext verwenden, um den Einstiegspunkt abzurufen, da er in SingletonComponent installiert ist. Wenn sich die Bindung, die Sie abrufen möchten, in ActivityComponent befand, verwenden Sie stattdessen ActivityContext.

Griff und Dolch

Hilt baut auf der Dagger-Abhängigkeitsinjektionsbibliothek auf und bietet eine Standardmethode zur Integration von Dagger in eine Android-Anwendung.

In Bezug auf Dagger sind die Ziele von Hilt:

  • Die Dagger-Infrastruktur für Android-Apps wird vereinfacht.
  • Es soll ein Standardsatz von Komponenten und Bereichen erstellt werden, um die Einrichtung, Lesbarkeit und Codefreigabe zwischen Anwendungen zu vereinfachen.
  • Um eine einfache Möglichkeit zu bieten, verschiedene Bindungen für verschiedene Build-Typen bereitzustellen, z. B. Tests, Fehlerbehebung oder Release.

Da das Android-Betriebssystem viele seiner eigenen Framework-Klassen instanziiert, müssen Sie für die Verwendung von Dagger in einer Android-App eine erhebliche Menge an Textbausteinen schreiben. Hilt reduziert den Boilerplate-Code bei der Verwendung von Dagger in einer Android-Anwendung. Hilt generiert automatisch Folgendes:

  • Komponenten zur Integration von Android-Framework-Klassen in Dagger, die Sie andernfalls manuell erstellen müssten.
  • Bereichsanmerkungen zur Verwendung mit den Komponenten, die von Hilt automatisch generiert werden.
  • Vordefinierte Bindungen zur Darstellung von Android-Klassen wie Application oder Activity.
  • Vordefinierte Qualifier zur Darstellung von @ApplicationContext und @ActivityContext.

Dagger- und Hilt-Code können in derselben Codebasis nebeneinander existieren. In den meisten Fällen ist es jedoch am besten, die gesamte Nutzung von Dagger unter Android mit Hilt zu verwalten. Informationen zum Migrieren eines Projekts, das Dagger zu Hilt verwendet, finden Sie in der Migrationsanleitung und im Codelab zum Migrieren von Dagger-Anwendungen zu Hilt.

Weitere Informationen

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

Produktproben

Codelabs

Blogs