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

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

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

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

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

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

Классный

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

Котлин

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

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

Классный

...
plugins {
  id 'com.google.devtools.ksp'
  id 'com.google.dagger.hilt.android'
}

android {
  ...
}

dependencies {
  implementation "com.google.dagger:hilt-android:2.57.1"
  ksp "com.google.dagger:hilt-compiler:2.57.1"
}

Котлин

plugins {
  id("com.google.devtools.ksp")
  id("com.google.dagger.hilt.android")
}

android {
  ...
}

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

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

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

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

Котлин

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

Java

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

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

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

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

Котлин

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

Java

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

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

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

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

Определение привязок Hilt

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

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

Котлин

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

Java

public class AnalyticsAdapter {

  private final AnalyticsService service;

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

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

модули Hilt

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

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

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

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

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

Аннотация @Binds указывает 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
}

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

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

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

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

Рассмотрим предыдущий пример. Если вы не являетесь непосредственным владельцем класса 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)
  }
}

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

Java

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

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

Вы можете указать необходимый тип, добавив к полю или параметру соответствующий квалификатор:

Котлин

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

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

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

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

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

Котлин

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

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

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

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

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

Компания 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
) { ... }

Java

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

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. FragmentComponent и ViewComponent находятся внутри ActivityComponent. ActivityComponent находится внутри ActivityRetainedComponent. ViewModelComponent находится внутри ActivityRetainedComponent. 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 { ... }

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

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

Котлин

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

Внедрение зависимостей в классы, не поддерживаемые 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
  }

  ...
}

Java

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

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

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

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

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

Что касается Даггера, то цели Хилта заключаются в следующем:

  • Для упрощения инфраструктуры, связанной с 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, ознакомьтесь со следующими дополнительными материалами.

Образцы

Кодлабс

Блоги