[Kotlin] Android 依賴注入 示範(使用 Hilt)

Dagger 是一個由 Google 維護,支援 Java 和 Kotlin 的依賴注入 (Dependency Injection, DI)套件,而 Hilt 則是基於 Dagger 的、專門針對 Android 平台的 DI 套件

安裝(使用 KSP)

在 build.gradle.kts(Project) 檔案下面加入 com.google.dagger.hilt.android :
(org.jetbrains.kotlin.android 和 com.google.devtools.ksp 也是必要的)
    
plugins {
    id("com.android.application") version "8.2.2" apply false
    id("org.jetbrains.kotlin.android") version "1.9.0" apply false
    id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false
    id("com.google.dagger.hilt.android") version "2.50" apply false
}
    

在 build.gradle.kts (Module :app) 的最上面,加入 com.google.dagger.hilt.android:
(com.google.devtools.ksp 也是必要的)
    
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp")
    id("com.google.dagger.hilt.android")
}
    

在 build.gradle.kts (Module :app) 的下半部,加入以下程式碼
    
dependencies {
    implementation("com.google.dagger:hilt-android:2.50")
    ksp ("com.google.dagger:hilt-android-compiler:2.50")
}
    

使用

使用 Hilt 的所有應用程式都必須包含標記 @HiltAndroidApp 的 Application 類別,這裡建立一個自訂的 Application 類別
    
@HiltAndroidApp
class MainApplication : Application() {
}
    

程式入口點要加上 @AndroidEntryPoint 標記:
    
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
}
    

然後在 AndroidManifest.xml 加上這一行:
    
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:name=".MainApplication"
        >
    </application>

</manifest>
    

這樣在 MainActivity 中就可以使用 DI,不過所有依賴 MainActivity 類別的其他類別不管有沒有使用 DI 都需要加上 @AndroidEntryPoint 標記,包含以下幾種:
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver
不過有兩種比較特別,需要使用特殊標記:
  • Application: @HiltAndroidApp
  • ViewModel: @HiltViewModel
直接來看一個最簡單的示範:

建立一個 UserService 服務:
(一定要加入 @Inject constructor() )
    
class UserService @Inject constructor() {
    fun getUserName(): String {
        return "Ruyut"
    }
}
    

在 MainActivity 中透過 DI 自動取得 UserService 實例:
    
@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    @Inject lateinit var userService: UserService

    private val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val userName = userService.getUserName()
        Log.i(TAG, "onCreate: $userName")
    }
}
    

(雖然說 Kotlin 的單例只要把 class 換成 object 即可,不過這裡只是簡單的示範)

Room 使用 Hilt DI 示範

假設我們有 AppDatabase :
    
@Database(
    entities = [
        UserEntity::class,
    ], version = 1
)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
    

我們可以建立 DatabaseModule.kt (通常會放在 di package 下) 來自訂該如何實例化:
    
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {

    @Singleton
    @Provides
    fun provideDataBase(@ApplicationContext context: Context): AppDatabase {
        return Room.databaseBuilder(
            context.applicationContext,
            AppDatabase::class.java,
            "Tasks.db"
        ).build()
    }
}
    

使用也非常簡單:
    
@Inject lateinit var userService: UserService
    



參考資料:
Dagger
Dagger KSP
Android Developers - Dependency injection in Android

留言