Внедрение зависимостей с помощью Hilt,Внедрение зависимостей с помощью Hilt

Hilt — это библиотека внедрения зависимостей для Android, которая упрощает шаблонное внедрение зависимостей вручную в ваш проект. Выполнение внедрения зависимостей вручную требует от вас создания каждого класса и его зависимостей вручную, а также использования контейнеров для повторного использования и управления зависимостями.

Hilt предоставляет стандартный способ использования внедрения зависимостей в вашем приложении, предоставляя контейнеры для каждого класса Android в вашем проекте и автоматически управляя их жизненными циклами. Hilt построен на основе популярной библиотеки DI Dagger, чтобы получить преимущества от корректности времени компиляции, производительности во время выполнения, масштабируемости и поддержки Android Studio , которые обеспечивает Dagger. Для получения дополнительной информации см. Рукоять и Кинжал .

В этом руководстве объясняются основные концепции Hilt и созданных им контейнеров. Он также включает демонстрацию того, как загрузить существующее приложение для использования Hilt.

Добавление зависимостей

Сначала добавьте плагин hilt-android-gradle-plugin в корневой файл build.gradle вашего проекта:

классный

plugins {
  ...
  id 'com.google.dagger.hilt.android' version '2.51.1' apply false
}

Котлин

plugins {
  ...
  id("com.google.dagger.hilt.android") version "2.51.1" apply false
}

Затем примените плагин Gradle и добавьте эти зависимости в файл app/build.gradle :

классный

...
plugins {
  id 'kotlin-kapt'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.51.1"
  kapt "com.google.dagger:hilt-compiler:2.51.1"
}

// Allow references to generated code
kapt {
  correctErrorTypes true
}

Котлин

plugins {
  id("kotlin-kapt")
  id("com.google.dagger.hilt.android")
}

android {
  ...
}

dependencies {
  implementation("com.google.dagger:hilt-android:2.51.1")
  kapt("com.google.dagger:hilt-android-compiler:2.51.1")
}

// Allow references to generated code
kapt {
  correctErrorTypes = true
}

Hilt использует функции Java 8 . Чтобы включить Java 8 в вашем проекте, добавьте следующее в файл app/build.gradle :

классный

android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Котлин

android {
  ...
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
  }
}

Класс применения рукояти

Все приложения, использующие Hilt, должны содержать класс Application , помеченный @HiltAndroidApp .

@HiltAndroidApp запускает генерацию кода Hilt, включая базовый класс для вашего приложения, который служит контейнером зависимостей на уровне приложения.

Котлин

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

Ява

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

Этот сгенерированный компонент Hilt прикрепляется к жизненному циклу объекта Application и предоставляет ему зависимости. Кроме того, это родительский компонент приложения, а это означает, что другие компоненты могут получить доступ к предоставляемым им зависимостям.

Внедрение зависимостей в классы Android

Как только Hilt настроен в вашем классе Application и доступен компонент уровня приложения, Hilt может предоставлять зависимости другим классам Android, имеющим аннотацию @AndroidEntryPoint :

Котлин

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

Ява

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

В настоящее время Hilt поддерживает следующие классы Android:

  • Application (с помощью @HiltAndroidApp )
  • ViewModel (с помощью @HiltViewModel )
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

Если вы аннотируете класс Android с помощью @AndroidEntryPoint , вам также необходимо аннотировать классы Android, которые от него зависят. Например, если вы аннотируете фрагмент, вы также должны аннотировать все действия, в которых вы используете этот фрагмент.

@AndroidEntryPoint создает отдельный компонент Hilt для каждого класса Android в вашем проекте. Эти компоненты могут получать зависимости от соответствующих родительских классов, как описано в разделе «Иерархия компонентов» .

Чтобы получить зависимости от компонента, используйте аннотацию @Inject для внедрения поля:

Котлин

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Ява

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Классы, которые внедряет Hilt, могут иметь другие базовые классы, которые также используют внедрение. Этим классам не нужна аннотация @AndroidEntryPoint если они абстрактные.

Дополнительные сведения о том, в какой обратный вызов жизненного цикла внедряется класс Android, см. в разделе Время жизни компонентов .

Определить привязки рукоятки

Чтобы выполнить внедрение полей, Hilt необходимо знать, как предоставить экземпляры необходимых зависимостей из соответствующего компонента. Привязка содержит информацию, необходимую для предоставления экземпляров типа в качестве зависимости.

Одним из способов предоставления информации о привязке в Hilt является внедрение конструктора . Используйте аннотацию @Inject в конструкторе класса, чтобы сообщить Hilt, как предоставлять экземпляры этого класса:

Котлин

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

Ява

public class AnalyticsAdapter {

  private final AnalyticsService service;

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

Параметры аннотированного конструктора класса являются зависимостями этого класса. В этом примере AnalyticsAdapter имеет AnalyticsService в качестве зависимости. Следовательно, Hilt также должен знать, как предоставлять экземпляры AnalyticsService .

Модули рукояти

Иногда тип невозможно внедрить с помощью конструктора. Это может произойти по нескольким причинам. Например, вы не можете внедрить интерфейс в конструктор. Вы также не можете внедрить в конструктор тип, которым вы не владеете, например класс из внешней библиотеки. В этих случаях вы можете предоставить Hilt информацию о привязке, используя модули Hilt .

Модуль Hilt — это класс, помеченный @Module . Как и модуль Dagger , он сообщает Hilt, как предоставлять экземпляры определенных типов. В отличие от модулей Dagger, вы должны пометить модули Hilt с помощью @InstallIn чтобы сообщить Hilt, в каком классе Android каждый модуль будет использоваться или установлен.

Зависимости, которые вы предоставляете в модулях Hilt, доступны во всех сгенерированных компонентах, связанных с классом Android, в котором вы устанавливаете модуль Hilt.

Внедрение экземпляров интерфейса с помощью @Binds

Рассмотрим пример AnalyticsService . Если AnalyticsService является интерфейсом, вы не можете внедрить его в конструктор. Вместо этого предоставьте Hilt информацию о привязке, создав абстрактную функцию с аннотацией @Binds внутри модуля Hilt.

Аннотация @Binds сообщает Hilt, какую реализацию использовать, когда необходимо предоставить экземпляр интерфейса.

Аннотированная функция предоставляет Hilt следующую информацию:

  • Тип возвращаемого значения функции сообщает Hilt, экземпляры какого интерфейса предоставляет функция.
  • Параметр функции сообщает Hilt, какую реализацию предоставить.

Котлин

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
}

Ява

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

Модуль Hilt AnalyticsModule помечен аннотацией @InstallIn(ActivityComponent.class) поскольку вы хотите, чтобы Hilt внедрил эту зависимость в ExampleActivity . Эта аннотация означает, что все зависимости в AnalyticsModule доступны во всех действиях приложения.

Внедрить экземпляры с помощью @Provides

Интерфейсы — не единственный случай, когда вы не можете внедрить тип в конструктор. Внедрение в конструктор также невозможно, если вы не являетесь владельцем класса, поскольку он взят из внешней библиотеки (такие классы, как Retrofit , OkHttpClient или базы данных Room ), или если экземпляры необходимо создавать с помощью шаблона компоновщика .

Рассмотрим предыдущий пример. Если вы не являетесь владельцем класса AnalyticsService напрямую, вы можете указать Hilt, как предоставлять экземпляры этого типа, создав функцию внутри модуля Hilt и аннотировав эту функцию @Provides .

Аннотированная функция предоставляет Hilt следующую информацию:

  • Тип возвращаемого значения функции сообщает Hilt, экземпляры какого типа предоставляет функция.
  • Параметры функции сообщают Hilt зависимости соответствующего типа.
  • Тело функции сообщает Hilt, как предоставить экземпляр соответствующего типа. Hilt выполняет тело функции каждый раз, когда ему необходимо предоставить экземпляр этого типа.

Котлин

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

Ява

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

Предоставьте несколько привязок для одного и того же типа.

В тех случаях, когда вам нужно, чтобы Hilt предоставил различные реализации одного и того же типа в качестве зависимостей, вы должны предоставить Hilt несколько привязок. Вы можете определить несколько привязок для одного и того же типа с помощью квалификаторов .

Квалификатор — это аннотация, которую вы используете для идентификации конкретной привязки для типа, если для этого типа определено несколько привязок.

Рассмотрим пример. Если вам нужно перехватить вызовы AnalyticsService , вы можете использовать объект OkHttpClient с перехватчиком . Для других услуг вам может потребоваться перехват вызовов другим способом. В этом случае вам нужно указать Hilt, как предоставить две разные реализации OkHttpClient .

Сначала определите квалификаторы, которые вы будете использовать для аннотирования методов @Binds или @Provides :

Котлин

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient

Ява

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface AuthInterceptorOkHttpClient {}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
private @interface OtherInterceptorOkHttpClient {}

Затем Hilt необходимо знать, как предоставить экземпляр типа, соответствующего каждому квалификатору. В этом случае вы можете использовать модуль Hilt с @Provides . Оба метода имеют один и тот же тип возвращаемого значения, но квалификаторы помечают их как две разные привязки:

Котлин

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

Ява

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

Вы можете ввести конкретный тип, который вам нужен, аннотируя поле или параметр соответствующим квалификатором:

Котлин

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

Ява

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

Рекомендуется, если вы добавляете квалификатор к типу, добавлять квалификаторы ко всем возможным способам обеспечения этой зависимости. Если оставить базовую или общую реализацию без квалификатора, это чревато ошибками и может привести к тому, что Hilt внедрит неправильную зависимость.

Предопределенные квалификаторы в Hilt

Hilt предоставляет некоторые предопределенные квалификаторы. Например, поскольку вам может понадобиться класс Context либо из приложения, либо из действия, Hilt предоставляет квалификаторы @ApplicationContext и @ActivityContext .

Предположим, что классу AnalyticsAdapter из примера нужен контекст действия. Следующий код демонстрирует, как предоставить контекст активности AnalyticsAdapter :

Котлин

class AnalyticsAdapter @Inject constructor(
    @ActivityContext private val context: Context,
    private val service: AnalyticsService
) { ... }

Ява

public class AnalyticsAdapter {

  private final Context context;
  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(
    @ActivityContext Context context,
    AnalyticsService service
  ) {
    this.context = context;
    this.service = service;
  }
}

Другие предопределенные привязки, доступные в Hilt, см. в разделе Привязки компонентов по умолчанию .

Сгенерированные компоненты для классов Android

Для каждого класса Android, в котором вы можете выполнять внедрение полей, существует связанный компонент Hilt, на который вы можете сослаться в аннотации @InstallIn . Каждый компонент Hilt отвечает за внедрение своих привязок в соответствующий класс Android.

Предыдущие примеры продемонстрировали использование ActivityComponent в модулях Hilt.

Hilt предоставляет следующие компоненты:

Компонент рукояти Инжектор для
SingletonComponent Application
ActivityRetainedComponent Н/Д
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View с аннотацией @WithFragmentBindings
ServiceComponent Service

Срок службы компонентов

Hilt автоматически создает и уничтожает экземпляры сгенерированных классов компонентов в соответствии с жизненным циклом соответствующих классов Android.

Сгенерированный компонент Создано в Разрушен в
SingletonComponent Application#onCreate() Application уничтожено
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel создана ViewModel уничтожена
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View уничтожено
ViewWithFragmentComponent View#super() View уничтожено
ServiceComponent Service#onCreate() Service#onDestroy()

Области компонентов

По умолчанию все привязки в Hilt не ограничены . Это означает, что каждый раз, когда ваше приложение запрашивает привязку, Hilt создает новый экземпляр нужного типа.

В этом примере каждый раз, когда Hilt предоставляет AnalyticsAdapter в качестве зависимости от другого типа или посредством внедрения поля (как в ExampleActivity ), Hilt предоставляет новый экземпляр AnalyticsAdapter .

Однако Hilt также позволяет ограничить привязку конкретным компонентом. Hilt создает привязку с ограниченной областью только один раз для каждого экземпляра компонента, областью действия которого является привязка, и все запросы к этой привязке используют один и тот же экземпляр.

В таблице ниже перечислены аннотации области действия для каждого сгенерированного компонента:

Android-класс Сгенерированный компонент Объем
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View с аннотацией @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

В этом примере, если вы привязываете AnalyticsAdapter к ActivityComponent с помощью @ActivityScoped , Hilt предоставляет один и тот же экземпляр AnalyticsAdapter на протяжении всего срока действия соответствующего действия:

Котлин

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

Ява

@ActivityScoped
public class AnalyticsAdapter {

  private final AnalyticsService service;

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

Предположим, что AnalyticsService имеет внутреннее состояние, которое требует, чтобы один и тот же экземпляр использовался каждый раз — не только в ExampleActivity , но и в любом месте приложения. В этом случае уместно ограничить область действия AnalyticsService SingletonComponent . В результате всякий раз, когда компоненту необходимо предоставить экземпляр AnalyticsService , он каждый раз предоставляет один и тот же экземпляр.

В следующем примере показано, как определить область привязки к компоненту в модуле Hilt. Область привязки должна соответствовать области действия компонента, в котором она установлена, поэтому в этом примере вы должны установить AnalyticsService в SingletonComponent вместо ActivityComponent :

Котлин

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

Ява

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

Чтобы узнать больше об областях действия компонентов Hilt, см. раздел «Область действия в Android и Hilt» .

Иерархия компонентов

Установка модуля в компонент позволяет получить доступ к его привязкам как к зависимости от других привязок в этом компоненте или в любом дочернем компоненте ниже него в иерархии компонентов:

ViewWithFragmentComponent находится в разделе FragmentComponent. ФрагментКомпонент     и ViewComponent находятся в разделе ActivityComponent. ActivityComponent находится под     АктивныйRetainedComponent. ViewModelComponent находится под     АктивныйRetainedComponent. ActivityRetainedComponent и ServiceComponent     находятся в SingletonComponent.
Рисунок 1. Иерархия компонентов, которые генерирует Hilt.

Привязки компонента по умолчанию

Каждый компонент Hilt поставляется с набором привязок по умолчанию, которые Hilt может внедрить в качестве зависимостей в ваши собственные привязки. Обратите внимание, что эти привязки соответствуют общим типам активности и фрагментов, а не какому-либо конкретному подклассу. Это связано с тем, что Hilt использует одно определение компонента действия для внедрения всех действий. Каждое действие имеет отдельный экземпляр этого компонента.

Android-компонент Привязки по умолчанию
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

Привязка контекста приложения также доступна с помощью @ApplicationContext . Например:

Котлин

class AnalyticsServiceImpl @Inject constructor(
  @ApplicationContext context: Context
) : AnalyticsService { ... }

// The Application binding is available without qualifiers.
class AnalyticsServiceImpl @Inject constructor(
  application: Application
) : AnalyticsService { ... }

Ява

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

Привязка контекста активности также доступна с помощью @ActivityContext . Например:

Котлин

class AnalyticsAdapter @Inject constructor(
  @ActivityContext context: Context
) { ... }

// The Activity binding is available without qualifiers.
class AnalyticsAdapter @Inject constructor(
  activity: FragmentActivity
) { ... }

Ява

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

Внедрение зависимостей в классы, не поддерживаемые Hilt

Hilt поддерживает наиболее распространенные классы Android. Однако вам может потребоваться выполнить внедрение полей в классы, которые Hilt не поддерживает.

В этих случаях вы можете создать точку входа, используя аннотацию @EntryPoint . Точка входа — это граница между кодом, управляемым Hilt, и кодом, который им не управляет. Это момент, когда код впервые попадает в граф объектов, которыми управляет Hilt. Точки входа позволяют Hilt использовать код, который Hilt не может обеспечить зависимостями в графе зависимостей.

Например, Hilt не поддерживает напрямую поставщиков контента . Если вы хотите, чтобы поставщик контента использовал Hilt для получения некоторых зависимостей, вам необходимо определить интерфейс с аннотацией @EntryPoint для каждого нужного типа привязки и включить квалификаторы. Затем добавьте @InstallIn , чтобы указать компонент, в котором необходимо установить точку входа, следующим образом:

Котлин

class ExampleContentProvider : ContentProvider() {

  @EntryPoint
  @InstallIn(SingletonComponent::class)
  interface ExampleContentProviderEntryPoint {
    fun analyticsService(): AnalyticsService
  }

  ...
}

Ява

public class ExampleContentProvider extends ContentProvider {

  @EntryPoint
  @InstallIn(SingletonComponent.class)
  interface ExampleContentProviderEntryPoint {
    public AnalyticsService analyticsService();
  }
  ...
}

Чтобы получить доступ к точке входа, используйте соответствующий статический метод из EntryPointAccessors . Параметр должен быть либо экземпляром компонента, либо объектом @AndroidEntryPoint , который действует как держатель компонента. Убедитесь, что компонент, который вы передаете в качестве параметра, и статический метод EntryPointAccessors соответствуют классу Android в аннотации @InstallIn интерфейса @EntryPoint :

Котлин

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

Ява

public class ExampleContentProvider extends ContentProvider {

  @Override
  public Cursor query(...) {
    Context appContext = getContext().getApplicationContext();
    ExampleContentProviderEntryPoint hiltEntryPoint =
      EntryPointAccessors.fromApplication(appContext, ExampleContentProviderEntryPoint.class);
    AnalyticsService analyticsService = hiltEntryPoint.analyticsService();
  }
}

В этом примере вы должны использовать ApplicationContext для получения точки входа, поскольку точка входа установлена ​​в SingletonComponent . Если бы привязка, которую вы хотели получить, находилась в ActivityComponent , вместо этого вы бы использовали ActivityContext .

Рукоять и кинжал

Hilt построен на основе библиотеки внедрения зависимостей Dagger , обеспечивая стандартный способ включения Dagger в приложение Android.

В отношении Dagger цели Hilt заключаются в следующем:

  • Упростить инфраструктуру, связанную с Dagger, для приложений Android.
  • Создать стандартный набор компонентов и областей действия для упрощения настройки, удобства чтения и совместного использования кода между приложениями.
  • Обеспечить простой способ предоставления различных привязок для различных типов сборок, таких как тестирование, отладка или выпуск.

Поскольку операционная система Android создает экземпляры многих собственных классов фреймворка, использование Dagger в приложении Android требует от вас написания значительного количества шаблонов. Hilt сокращает стандартный код, используемый при использовании Dagger в приложении Android. Hilt автоматически генерирует и предоставляет следующее:

  • Компоненты для интеграции классов платформы Android с Dagger, которые в противном случае вам пришлось бы создавать вручную.
  • Аннотации области действия для использования с компонентами, которые Hilt генерирует автоматически.
  • Предопределенные привязки для представления классов Android, таких как Application или Activity .
  • Предопределенные квалификаторы для представления @ApplicationContext и @ActivityContext .

Код Dagger и Hilt может сосуществовать в одной кодовой базе. Однако в большинстве случаев лучше всего использовать Hilt для управления всем использованием Dagger на Android. Чтобы перенести проект, использующий Dagger, на Hilt, см. руководство по миграции и лабораторную работу «Миграция вашего приложения Dagger на Hilt» .

Дополнительные ресурсы

Чтобы узнать больше о Hilt, посетите следующие дополнительные ресурсы.

Образцы

Кодлабы

Блоги