Injeksi dependensi dengan Hilt

Hilt adalah library injeksi dependensi untuk Android yang mengurangi boilerplate ketika melakukan injeksi dependensi manual dalam project Anda. Dengan melakukan injeksi dependensi manual, Anda harus membuat setiap class dan dependensinya secara manual, serta menggunakan container untuk menggunakan kembali dan mengelola dependensi.

Hilt menyediakan cara standar untuk menggunakan DI dalam aplikasi Anda dengan menyediakan container untuk setiap class Android dalam project Anda dan mengelola siklus prosesnya secara otomatis. Hilt ditambahkan pada library DI yang populer Dagger untuk mendapatkan manfaat dari ketepatan waktu kompilasi, performa runtime, skalabilitas, dan dukungan Android Studio yang disediakan oleh Dagger. Untuk informasi lebih lanjut, lihat Hilt dan Dagger.

Panduan ini menjelaskan konsep dasar Hilt dan container yang dihasilkannya. Panduan ini juga mencakup demo cara melakukan bootstrap pada aplikasi yang sudah ada untuk menggunakan Hilt.

Menambahkan dependensi

Pertama, tambahkan plugin hilt-android-gradle-plugin ke file build.gradle root project Anda:

Groovy

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
}

Kemudian, terapkan plugin Gradle dan tambahkan dependensi ini di file app/build.gradle Anda:

Groovy

...
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 menggunakan fitur Java 8. Untuk mengaktifkan Java 8 dalam project Anda, tambahkan kode berikut ke file app/build.gradle:

Groovy

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

Kotlin

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

Class aplikasi Hilt

Semua aplikasi yang menggunakan Hilt harus berisi class Application yang dianotasi dengan @HiltAndroidApp.

@HiltAndroidApp memicu pembuatan kode Hilt, termasuk class dasar untuk aplikasi Anda yang berfungsi sebagai container dependensi tingkat aplikasi.

Kotlin

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

Java

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

Komponen Hilt yang dihasilkan ini dipasang ke siklus proses objek Application dan menyediakan dependensi ke objek tersebut. Selain itu, komponen ini adalah komponen induk dari aplikasi, yang berarti bahwa komponen lain dapat mengakses dependensi yang disediakannya.

Menginjeksikan dependensi ke class Android

Setelah Hilt disiapkan di class Application Anda dan komponen tingkat aplikasi tersedia, Hilt dapat menyediakan dependensi ke class Android lain yang memiliki anotasi @AndroidEntryPoint:

Kotlin

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

Java

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

Saat ini Hilt mendukung class Android berikut:

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

Jika Anda menganotasi class Android dengan @AndroidEntryPoint, Anda juga harus menganotasi class Android yang bergantung pada class tersebut. Misalnya, jika menganotasi sebuah fragmen, Anda juga harus menganotasi setiap aktivitas yang menggunakan fragmen tersebut.

@AndroidEntryPoint menghasilkan komponen Hilt individual untuk setiap class Android dalam project Anda. Komponen ini dapat menerima dependensi dari class induk mereka masing-masing, seperti yang dijelaskan dalam Hierarki komponen.

Untuk mendapatkan dependensi dari komponen, gunakan anotasi @Inject untuk melakukan injeksi kolom:

Kotlin

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Java

@AndroidEntryPoint
public class ExampleActivity extends AppCompatActivity {

  @Inject
  AnalyticsAdapter analytics;
  ...
}

Class yang diinjeksikan oleh Hilt dapat memiliki class dasar lainnya yang juga menggunakan injeksi. Class tersebut tidak memerlukan anotasi @AndroidEntryPoint jika bersifat abstrak.

Untuk mempelajari lebih lanjut callback siklus proses tempat class Android diinjeksikan, lihat Masa aktif komponen.

Menentukan binding Hilt

Untuk melakukan injeksi kolom, Hilt perlu mengetahui cara menyediakan instance dependensi yang diperlukan dari komponen yang sesuai. Binding berisi informasi yang diperlukan untuk menyediakan instance suatu jenis sebagai dependensi.

Salah satu cara untuk memberikan informasi binding ke Hilt adalah injeksi konstruktor. Gunakan anotasi @Inject pada konstruktor class untuk memberi tahu Hilt cara menyediakan instance class tersebut:

Kotlin

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

Java

public class AnalyticsAdapter {

  private final AnalyticsService service;

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

Parameter dari konstruktor anotasi sebuah class adalah dependensi class tersebut. Dalam contoh ini, AnalyticsAdapter memiliki AnalyticsService sebagai dependensi. Oleh karena itu, Hilt juga harus mengetahui cara menyediakan instance AnalyticsService.

Modul Hilt

Terkadang suatu jenis tidak dapat di-injeksikan melalui konstruktor. Hal ini dapat terjadi karena beberapa alasan. Misalnya, Anda tidak dapat menginjeksikan antarmuka melalui konstruktor. Anda juga tidak dapat menginjeksikan jenis yang tidak Anda miliki melalui konstruktor, seperti class dari library eksternal. Dalam hal ini, Anda dapat memberikan informasi binding kepada Hilt dengan menggunakan modul Hilt.

Modul Hilt adalah class yang dianotasi dengan @Module. Seperti modul Dagger, modul ini memberi tahu Hilt cara menyediakan instance jenis tertentu. Tidak seperti modul Dagger, Anda harus menganotasikan modul Hilt dengan @InstallIn untuk memberi tahu Hilt class Android mana yang akan digunakan atau dipasang dalam setiap modul.

Dependensi yang Anda berikan di modul Hilt tersedia di semua komponen yang dihasilkan dan dikaitkan dengan class Android tempat Anda memasang modul Hilt.

Menginjeksikan instance antarmuka dengan @Binds

Pertimbangkan contoh AnalyticsService. Jika AnalyticsService adalah antarmuka, Anda tidak dapat menginjeksikannya melalui konstruktor. Sebagai gantinya, berikan informasi binding kepada Hilt dengan cara membuat fungsi abstrak yang dianotasi dengan @Binds di dalam modul Hilt.

Anotasi @Binds akan memberi tahu Hilt implementasi mana yang akan digunakan ketika Hilt harus menyediakan instance antarmuka.

Fungsi yang dianotasi menyediakan informasi berikut kepada Hilt:

  • Fungsi jenis nilai yang ditampilkan memberi tahu Hilt antarmuka mana yang instance-nya disediakan oleh fungsi.
  • Parameter fungsi memberi tahu Hilt implementasi mana yang harus disediakan.

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

Modul Hilt AnalyticsModule dianotasi dengan @InstallIn(ActivityComponent.class) karena Anda ingin Hilt menginjeksikan dependensi tersebut ke ExampleActivity. Anotasi ini berarti bahwa semua dependensi dalam AnalyticsModule tersedia di semua aktivitas aplikasi.

Menginjeksikan instance dengan @Provides

Antarmuka bukanlah satu-satunya kasus saat Anda tidak dapat memasukkan jenis melalui konstruktor. Injeksi Konstruktor juga tidak dapat dilakukan jika Anda bukan pemilik class karena class itu berasal dari library eksternal (class seperti Retrofit, OkHttpClient, atau database Room), atau jika instance harus dibuat dengan pola builder.

Pertimbangkan contoh sebelumnya. Jika Anda tidak secara langsung memiliki class AnalyticsService, Anda dapat memberi tahu Hilt cara menyediakan instance jenis ini dengan cara membuat fungsi di dalam modul Hilt dan menganotasi fungsi itu dengan @Provides.

Fungsi yang dianotasi menyediakan informasi berikut kepada Hilt:

  • Fungsi jenis nilai yang ditampilkan memberi tahu Hilt antarmuka mana yang instance-nya disediakan oleh fungsi.
  • Parameter fungsi memberi tahu Hilt dependensi dari jenis yang sesuai.
  • Isi fungsi memberi tahu Hilt cara menyediakan instance dari jenis yang sesuai. Hilt menjalankan isi fungsi setiap kali harus menyediakan instance dari jenis tersebut.

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

Menyediakan beberapa binding untuk jenis yang sama

Ketika Anda membutuhkan Hilt untuk menyediakan berbagai implementasi dari jenis yang sama sebagai dependensi, Anda harus memberikan beberapa binding kepada Hilt. Anda dapat menentukan beberapa binding untuk jenis yang sama dengan penentu.

Penentu adalah anotasi yang Anda gunakan agar dapat mengidentifikasi binding tertentu untuk suatu jenis ketika jenis itu memiliki beberapa binding yang telah ditetapkan.

Pertimbangkan contohnya. Jika Anda perlu meng-intercept panggilan ke AnalyticsService, Anda dapat menggunakan objek OkHttpClient dengan interceptor. Untuk layanan lain, Anda mungkin perlu meng-intercept panggilan dengan cara lain. Dalam hal ini, Anda perlu memberi tahu Hilt cara menyediakan dua implementasi OkHttpClient yang berbeda.

Pertama, tentukan penentu yang akan Anda gunakan untuk menganotasi metode @Binds atau @Provides:

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

Kemudian, Hilt perlu mengetahui cara menyediakan instance dari jenis yang sesuai dengan setiap penentu. Dalam hal ini, Anda dapat menggunakan modul Hilt dengan @Provides. Kedua metode memiliki persamaan dalam jenis nilai yang ditampilkan, tetapi penentu melabelinya sebagai dua binding yang berbeda:

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

Anda dapat menginjeksikan jenis tertentu yang dibutuhkan dengan menganotasi kolom atau parameter dengan penentu yang sesuai:

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

Sebagai praktik terbaik, jika Anda menambahkan penentu ke suatu jenis, tambahkan penentu ke semua cara yang memungkinkan untuk menyediakan dependensi tersebut. Membiarkan implementasi dasar atau umum tanpa penentu akan rawan terhadap error dan dapat menyebabkan Hilt memasukkan dependensi yang salah.

Penentu yang telah ditetapkan dalam Hilt

Hilt menyediakan beberapa penentu yang telah ditetapkan. Misalnya, karena Anda mungkin memerlukan class Context dari aplikasi atau aktivitas, Hilt menyediakan penentu @ApplicationContext dan @ActivityContext.

Misalkan class AnalyticsAdapter dari contoh membutuhkan konteks aktivitas. Kode berikut menunjukkan cara menyediakan konteks aktivitas untuk AnalyticsAdapter:

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

Untuk binding lain yang telah ditetapkan sebelumnya dan tersedia di Hilt, lihat Binding default komponen.

Komponen yang dihasilkan untuk class Android

Untuk setiap class Android tempat Anda dapat melakukan injeksi kolom, ada komponen Hilt terkait yang dapat Anda lihat dalam anotasi @InstallIn. Setiap komponen Hilt bertanggung jawab untuk menginjeksikan binding ke kelas Android yang sesuai.

Contoh sebelumnya menunjukkan penggunaan ActivityComponent modul dalam Hilt.

Hilt memberikan komponen berikut:

Komponen Hilt Injektor untuk
SingletonComponent Application
ActivityRetainedComponent T/A
ViewModelComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View dianotasi dengan @WithFragmentBindings
ServiceComponent Service

Masa aktif komponen

Hilt secara otomatis membuat dan menghancurkan instance dari class komponen yang dihasilkan setelah siklus proses class Android terkait.

Komponen yang dihasilkan Dibuat pada Dihancurkan pada
SingletonComponent Application#onCreate() Application dihancurkan
ActivityRetainedComponent Activity#onCreate() Activity#onDestroy()
ViewModelComponent ViewModel dibuat ViewModel dihancurkan
ActivityComponent Activity#onCreate() Activity#onDestroy()
FragmentComponent Fragment#onAttach() Fragment#onDestroy()
ViewComponent View#super() View dihancurkan
ViewWithFragmentComponent View#super() View dihancurkan
ServiceComponent Service#onCreate() Service#onDestroy()

Cakupan komponen

Secara default, semua binding di Hilt adalah tanpa cakupan. Ini berarti setiap kali aplikasi Anda meminta binding, Hilt membuat instance baru dari jenis yang diperlukan.

Dalam contoh ini, setiap kali Hilt memberikan AnalyticsAdapter sebagai dependensi ke jenis lain atau melalui injeksi kolom (seperti di ExampleActivity), Hilt memberikan instance baru AnalyticsAdapter.

Namun, Hilt juga memungkinkan binding untuk tercakup dalam komponen tertentu. Hilt hanya membuat binding cakupan sekali saja per instance dari komponen tempat binding tercakup, dan semua permintaan untuk binding itu memiliki instance yang sama.

Tabel di bawah mencantumkan anotasi cakupan untuk setiap komponen yang dihasilkan:

Class Android Komponen yang dihasilkan Cakupan
Application SingletonComponent @Singleton
Activity ActivityRetainedComponent @ActivityRetainedScoped
ViewModel ViewModelComponent @ViewModelScoped
Activity ActivityComponent @ActivityScoped
Fragment FragmentComponent @FragmentScoped
View ViewComponent @ViewScoped
View dianotasi dengan @WithFragmentBindings ViewWithFragmentComponent @ViewScoped
Service ServiceComponent @ServiceScoped

Dalam contoh ini, jika Anda cakupkan AnalyticsAdapter ke ActivityComponent menggunakan @ActivityScoped, Hilt akan memberikan instance AnalyticsAdapter yang sama selama masa aktif aktivitas yang terkait:

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

Misalkan AnalyticsService memiliki status internal yang mewajibkan instance yang sama digunakan setiap saat—bukan hanya di ExampleActivity, tetapi di mana saja dalam aplikasi. Dalam hal ini, cakupan AnalyticsService sesuai dengan SingletonComponent. Hasilnya, setiap kali komponen perlu menyediakan instance AnalyticsService, komponen akan menyediakan instance yang sama setiap saat.

Contoh berikut menunjukkan cara mengatur cakupan binding ke komponen dalam modul Hilt. Cakupan binding harus cocok dengan cakupan komponen tempatnya diinstal, sehingga dalam contoh ini Anda harus menginstal AnalyticsService di SingletonComponent, bukan ActivityComponent:

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

Untuk mempelajari cakupan komponen Hilt lebih lanjut, lihat Cakupan di Android dan Hilt.

Hierarki komponen

Menginstal modul ke dalam komponen memungkinkan binding-nya untuk diakses sebagai dependensi binding lain dalam komponen itu atau dalam komponen turunan di bawahnya dalam hierarki komponen:

ViewWithFragmentComponent berada di bawah FragmentComponent. FragmentComponent
    dan ViewComponent berada di bawah ActivityComponent. ActivityComponent berada di bawah
    ActivityRetainedComponent. ViewModelComponent berada di bawah
    ActivityPersistedComponent. ActivityPersistedComponent dan ServiceComponent
    berada di bawah SingletonComponent.
Gambar 1. Hierarki komponen yang dihasilkan Hilt.

Binding default komponen

Setiap komponen Hilt dilengkapi dengan serangkaian binding default yang dapat diinjeksikan oleh Hilt sebagai dependensi ke binding kustom Anda sendiri. Perhatikan bahwa binding ini sesuai dengan aktivitas umum dan jenis fragmen, bukan dengan subclass tertentu. Ini karena Hilt menggunakan definisi komponen aktivitas tunggal untuk menginjeksikan semua aktivitas. Setiap aktivitas memiliki instance yang berbeda dari komponen ini.

Komponen Android Binding default
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

Binding konteks aplikasi juga tersedia menggunakan @ApplicationContext. Contoh:

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

Binding konteks aktivitas juga tersedia menggunakan @ActivityContext. Contoh:

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

Menginjeksikan dependensi dalam class yang tidak didukung oleh Hilt

Hilt dilengkapi dengan dukungan untuk class Android yang paling umum. Namun, Anda mungkin perlu melakukan injeksi kolom di kelas yang tidak didukung oleh Hilt.

Dalam kasus itu, Anda dapat membuat titik entri menggunakan anotasi @EntryPoint. Titik entri adalah batas antara kode yang dikelola oleh Hilt dan yang tidak. Ini adalah titik di mana kode pertama kali masuk ke grafik objek yang dikelola Hilt. Titik masuk memungkinkan Hilt untuk menggunakan kode yang tidak dikelola Hilt untuk menyediakan dependensi dalam grafik dependensi.

Misalnya, Hilt tidak mendukung penyedia konten secara langsung. Jika Anda ingin penyedia konten menggunakan Hilt untuk mendapatkan beberapa dependensi, Anda perlu menentukan antarmuka yang dianotasi dengan @EntryPoint untuk setiap jenis binding yang diinginkan dan menyertakan penentu. Lalu tambahkan @InstallIn untuk menentukan komponen tempat menginstal titik masuk sebagai berikut:

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

Untuk mengakses titik masuk, gunakan metode statis yang sesuai dari EntryPointAccessors. Parameter harus berupa instance komponen atau objek @AndroidEntryPoint yang berfungsi sebagai pemegang komponen. Pastikan bahwa komponen yang Anda teruskan sebagai parameter dan metode statis EntryPointAccessors cocok dengan class Android dalam anotasi @InstallIn pada antarmuka @EntryPoint:

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

Dalam contoh ini, Anda harus menggunakan ApplicationContext untuk mengambil titik masuk karena titik masuk diinstal di SingletonComponent. Jika binding yang ingin Anda ambil berada di ActivityComponent, Anda perlu menggunakan ActivityContext sebagai gantinya.

Hilt dan Dagger

Hilt dibangun pada library injeksi dependensi Dagger, yang menyediakan cara standar untuk memasukkan Dagger ke aplikasi Android.

Berkaitan dengan Dagger, tujuan Hilt adalah sebagai berikut:

  • Menyederhanakan infrastruktur yang berkaitan dengan Dagger untuk aplikasi Android.
  • Membuat serangkaian komponen dan cakupan standar untuk memudahkan penyiapan, keterbacaan, dan berbagi kode antar-aplikasi.
  • Memberikan cara yang mudah dalam menyediakan binding yang berbeda untuk berbagai jenis build, seperti pengujian, debug, atau rilis.

Karena sistem operasi Android membuat instance untuk berbagai class framework-nya sendiri, menggunakan Dagger di aplikasi Android mengharuskan Anda untuk menulis sejumlah besar boilerplate. Hilt mengurangi kode boilerplate yang terlibat dalam penggunaan Dagger pada aplikasi Android. Secara otomatis Hilt menghasilkan dan menyediakan hal berikut:

  • Komponen untuk mengintegrasikan class framework Android dengan Dagger yang seharusnya Anda buat sendiri.
  • Anotasi cakupan yang akan digunakan bersama komponen yang dihasilkan Hilt secara otomatis.
  • Binding yang telah ditetapkan sebelumnya untuk mewakili class Android seperti Application atau Activity.
  • Penentu yang telah ditetapkan sebelumnya untuk mewakili @ApplicationContext dan @ActivityContext.

Kode Dagger dan Hilt dapat berdampingan dalam codebase yang sama. Namun, dalam sebagian besar kasus, sebaiknya gunakan Hilt untuk mengelola semua penggunaan Dagger di Android. Untuk memigrasikan project yang menggunakan Dagger ke Hilt, lihat panduan migrasi dan Memigrasikan aplikasi Dagger ke codelab Hilt.

Referensi lainnya

Untuk mempelajari Hilt lebih lanjut, lihat referensi tambahan berikut.

Contoh

Codelab

Blog