Membangun Struktur Kode yang Reusable, Readable dan Testable dengan Dependency Injection di Kotlin

Ichwan Sholihin
4 min readSep 12, 2023
Photo by Mika Baumeister on Unsplash

Halo semua, pada artikel kali ini saya akan membahas salah satu komponen penting dalam membangun struktur kode yang baik terutama pengimplementasiannya saat pengembangan aplikasi, yakni Dependency Injection

Dependency Injection (DI) adalah salah satu teknik dalam penulisan struktur kode yang paling umum digunakan dalam pengembangan aplikasi Android. Penerapan DI memberikan anda keuntungan seperti kode yang reusable (dapat digunakan kembali), readable (mudah dibaca) dan testable (mudah diuji) ketika anda mengimplementasikan unit atau instrument testing dalam pengembangannya. Dalam pengembangan aplikasi Android, terdapat beberapa library DI yang populer seperti Dagger-Hilt dan Koin. Namun, pada artikel ini saya hanya akan membahas implementasi dasar dari penggunaan Dagger-Hilt pada beberapa contoh kasus.

Penerapan Hilt pada Android

Hilt adalah library DI untuk Android yang dapat mengurangi ketergantungan dalam melakukan DI secara manual. Dependency Injection yang dilakukan secara manual biasanya mengharuskan anda untuk membuat variabel atau class yang harus diimplementasikan setiap kali class lain membutuhkannya, hal ini justru menimbulkan boilerplate atau redundansi kode yang sulit untuk di maintain. Hilt menyediakan cara standar dalam mengimplementasikan DI pada setiap class dengan mengatur scope baik pada satu aplikasi maupun pada setiap views.

Untuk menerapkan Hilt pada Android, anda harus menginstal plugin pada Gradle. First thing first, tambahkan plugin hilt pada file build.gradle(project)

plugins {
id("com.android.application") version "8.1.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.0" apply false
//add this plugin
id("com.google.dagger.hilt.android") version "2.44" apply false
}

Kemudian, tambahkan juga plugin dan library pada build.gradle(module)

plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
kotlin("kapt")
id("com.google.dagger.hilt.android")
}

android {
namespace = "com.ichwan.arch.designpattern"
compileSdk = 33

defaultConfig {
applicationId = "com.ichwan.arch.designpattern"
minSdk = 24
targetSdk = 33
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

//..

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}

dependencies {

//..

//add this library
implementation("com.google.dagger:hilt-android:2.44")
kapt("com.google.dagger:hilt-android-compiler:2.44")

//for ViewModel
implementation("androidx.fragment:fragment-ktx:1.6.1")
implementation("androidx.activity:activity-ktx:1.7.2")

//...
}

Karena Hilt berjalan dengan annotation, penting untuk menambahkan plugin kotlin(“kapt”) dan pastikan bagi anda untuk menerapkan Java versi 17. Jika anda masih menerapkan Java 1.8 maka plugin dari Hilt tidak akan kompitable dengan sistem Java 8 dan muncul error seperti berikut.

Perhatikan juga untuk mengubah Gradle JDK menggunakan versi Java 17. (Lihat menu Settings->Build, Execution, Deployment->Build Tools->Gradle)

Lalu, buatlah satu class untuk menginisalisasi penggunaan Dagger-Hilt dan tambahkan anotasi @HiltAndroidApp

@HiltAndroidApp
class MyApplication: Application()

Kemudian registrasikan class MyApplication pada file android manifest dengan menambahkan attribute android:name

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application
//register here
android:name=".hilt.MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.DesignPattern"
tools:targetApi="31">

<activity
android:name=".hilt.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

Jika kita tidak menambahkan name pada manifest, akan terjadi force close ketika aplikasi dijalankan.

Contoh Implementasi Hilt

Bagi pemula, penerapan Hilt pada Android sangat memerlukan waktu untuk memahami praktik baiknya karena Hilt bekerja dengan anotasi yang diterapkan baik itu pada activity, fragment, viewmodel, service maupun broadcast receiver. Pertama kali, anda akan bingung bagaimana bisa sebuah variable ketika di panggil pada activity dapat menampilkan sebuah value, padahal dari deklarasi variable atau properties tersebut tidak mendeklarasikan nilai apapun, namun begitulah cara Hilt bekerja. Hilt dapat mengurangi boilerplate pada kode hanya dengan menerapkan anotasi pada variabel dari sebuah object yang sudah dideklarasikan dari class lain.

Untuk lebih jelasnya, buatlah object baru pada Kotlin dengan nama AppModule

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

@Singleton
@Provides
fun provideString() = "This is a string we will inject from AppModule"

}

Anotasi @Module wajib diimplementasikan pada setiap object atau instance yang akan menerapkan Hilt, dibawahnya anda juga harus memberitahu anotasi @Module terkait dimana module hilt akan diterapkan. Pada contoh diatas, SingletonComponent::class pada anotasi @InstallIn akan membertahu module bahwa Hilt diimplementasikan pada scope Application (atau dalam satu project). Pada Dagger versi 2.31, ApplicationComponent resmi dihapus dan anda dapat menggunakan alternatifnya seperti SingletonComponent

Pada fungsi provideString(), terdapat dua anotasi tambahan yakni @Singleton sesuai dengan class component dan @Provides yang menandakan bahwa fungsi tersebut akan menyediakan nilai ketika dipanggil. Berikutnya, terapkan fungsi provideString() pada class MainActivity

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

@Inject
lateinit var textString: String

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

Log.d("MainActivity","text string: $textString")
}
}

Kelas MainActivity ditandai dengan anotasi @AndroidEntryPoint, yang mengindikasikan bahwa kelas ini adalah bagian dari komponen Dagger Hilt dalam aplikasi Android. Hal ini memungkinkan anda untuk melakukan Dependency Injection (DI) pada class member ini. Selanjutnya, dalam kelas MainActivity, terdapat deklarasi anggota variabel textString yang diberi anotasi @Inject. Ini berarti variabel textString akan mendapatkan nilai dari dependensi yang telah diatur sebelumnya dalam AppModule. Selanjutnya, activity mencetak teks “text string: $textString” ke log menggunakan Log.d. Untuk pengecekannya anda dapat menjalankan project dan melihat Log dan dapat anda lihat bahwa dengan adanya anotasi @Inject nilai yang terdapat pada AppModule dapat dipanggil ke class MainActivity tanpa melakukan injection secara manual.

Untuk penerapan lebih lanjut, anda dapat membaca dokumentasi resmi dari Dagger-Hilt disini. Atau anda juga dapat menerapkan library DI yang lain seperti Koin pada dokumentasi berikut https://insert-koin.io/.

--

--