Внедрение зависимостей с помощью 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.56.2' apply false
}

Котлин

plugins {
  ...
  id("com.google.dagger.hilt.android") version "2.56.2" 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.56.2"
  ksp "com.google.dagger:hilt-compiler:2.56.2"
}

Котлин

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 использует функции 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 ), или если экземпляры должны быть созданы с помощью шаблона 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)
  }
}

Ява

@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 с interceptor . Для других служб вам может потребоваться перехватывать вызовы другим способом. В этом случае вам нужно указать 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 являются unscoped . Это означает, что каждый раз, когда ваше приложение запрашивает привязку, Hilt создает новый экземпляр нужного типа.

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

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

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

класс андроида Сгенерированный компонент Объем
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. 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 { ... }

Ява

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.

Что касается кинжала, цели 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, ознакомьтесь со следующими дополнительными ресурсами.

Образцы

Кодовые лаборатории

Блоги