Hilt - Ki-Kobayashi/Android-Wiki GitHub Wiki

🟩 Hiltの解説(公式)

https://developer.android.com/training/dependency-injection/hilt-android?hl=ja#hilt-and-dagger

🟩 Hiltの導入

※事前にKSPを導入しておく https://developer.android.com/training/dependency-injection/hilt-android?hl=ja

🟡app / build.gradle

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")// https://github.com/google/secrets-gradle-plugin/tree/main
    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    id("com.google.devtools.ksp")
    id("com.google.dagger.hilt.android") 👈追加
}

dependencies {
    ・・・
    // TODO: 🚨Composeで使用するときは、**「2.48以上」**にしないとエラーがになる
    val hilt_version = "2.48"
    implementation("com.google.dagger:hilt-android:$hilt_version")
    ksp("com.google.dagger:hilt-android-compiler:$hilt_version")
    implementation("androidx.hilt:hilt-navigation-compose:1.1.0")

🟡proj / build.gradle

plugins {
    id("com.android.application") version "8.2.0" apply false
    id("org.jetbrains.kotlin.android") version "1.9.0" apply false
    id("com.google.dagger.hilt.android") version "2.48" apply false 👈追加
    id("com.google.devtools.ksp") version "1.9.0-1.0.12"
}

 

🟩 Applicationクラスを追加する

@HiltAndroidApp
class JetTodoApp : Application() {
}

🟡Manifestにも追加

    <application
        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.JetTodo2"
        tools:targetApi="34"
        android:name=".JetTodoApp"> 👈追加

🟩 クラス名の上にアノテーションの追加

  • @AndroidEntryPoint
    • Activity
    • Fragment
    • View
    • Service
    • BroadcastReceiver
  • @HiltViewModel
    • ViewModel
  • @Module@InstallIn(XxxxComponent::class)
    • XxxxModuleクラス(インスタンスの作成方法を知らせるクラス※後述)
  • @EntryPoint@InstallIn(XxxxComponent::class)

🟡

🟩 @Module(Hiltモジュール) / モジュール内で使用するアノテーション

https://developer.android.com/training/dependency-injection/hilt-android?hl=ja#hilt-modules

🟡@Module が 必要なのは、以下の2つのケース

  • インターフェースの場合
       → コンストラクタ インジェクションができない(作り方しらない)ため、DIするには作り方を教える必要がある
       → @Binds を使用
       → 例: Room使用時の Repository/RepositoryImpl

.

  • 外部ライブラリのクラスなど、自分が所有していない型の場合
       → コンストラクタ インジェクションができない(作り方しらない)ため、DIするには作り方を教える必要がある
       → @Provides を使用
       → 例: Room, Retrofit, Moshiなどのライブラリのインスタンス生成、Builder パターンでインスタンスを作成する必要がある場合

.

🟡 @Provides

https://developer.android.com/training/dependency-injection/hilt-android?hl=ja#inject-provides
このアノテーションつけた関数の型のインスタンスを作成したときに、自動的に関数内の処理もやるよっていう指定

@Module
@InstallIn(SingletonComponent::class)
object ApiModule {
    /**
     * Retrofit2でデバッグ用にログ出力する設定
     *   https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor
     *   https://square.github.io/okhttp/features/interceptors/
     *
     * ◆ログレベル
     *      logger.levelで渡しているlevelには4種あり、出力の詳細さが変わる
     *      ★ NONE
     *        ログを出力しない
     *      ★ BASIC
     *        リクエストラインとレスポンスラインのみを出力
     *      ★ HEADER
     *        BASICのログに加えてリクエストヘッダとレスポンスヘッダが出力される
     *      ★ BODY
     *        HEADERのログに加えて、クエストボディとレスポンスボディが出力される
     */
    @Singleton
    @Provides
    fun provideOkhttpClient(): OkHttpClient {
        val logInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        return OkHttpClient.Builder()
            .addInterceptor {
                // 💡【下記処理について】
                //     特にリクエストに変更を加えたいが元のリクエストを変更せずに残したい場合に有用な処理

                // リクエストからHTTPのURLを取得
                val httpUrl = it.request().url
                // 元のリクエストからコピーを作成し、url(httpUrl)メソッドを使用して新しいURLに更新
                val requestBuilder = it.request().newBuilder().url(httpUrl)
                // 更新されたリクエストを使って処理を継続
                it.proceed(requestBuilder.build())
            }
            .addInterceptor(logInterceptor)
            .readTimeout(10, TimeUnit.SECONDS)
            .connectTimeout(10, TimeUnit.SECONDS)
            .build()
    }


    @Singleton
    @Provides
    fun provideMoshi(): MoshiConverterFactory {
        val moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()
        return MoshiConverterFactory.create(moshi)
    }

    @Singleton
    @Provides
    fun provideRetrofit(moshiConverterFactory: MoshiConverterFactory): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BuildConfig.BASE_URL)
            .addConverterFactory(moshiConverterFactory)
            .build()
    }

    @Singleton
    @Provides
    fun provideNewsApiService(retrofit: Retrofit): NewsApiService =
        retrofit.create(NewsApiService::class.java)
}

🟡 @Binds

https://developer.android.com/training/dependency-injection/hilt-android?hl=ja#inject-interfaces
インターフェースの場合は、コンストラクタ インジェクションができません。
代わりに、Hilt モジュール内に @Binds アノテーションを付けた抽象関数を作成する

  • 関数の戻り値の型で、その関数が提供するインターフェースのインスタンスを知らせます。
  • 関数のパラメータで、提供する実装を知らせます
🟡インターフェースの実装クラス
class AnalyticsServiceImpl @Inject constructor(
  ...
) : AnalyticsService { ... }

🟡Moduleクラス
@Module
@InstallIn(ActivityComponent::class) 👈AnalyticsModule 内のすべての依存関係が、アプリの"Activity内のみ(すべて)"で使用できるという意味
abstract class AnalyticsModule {

  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsService
}

.

🟡 @InstallIn アノテーション

App内のどの範囲のクラスにDIするかを限定するやつ

https://developer.android.com/training/dependency-injection/hilt-android?hl=ja#component-default
https://developer.android.com/training/dependency-injection/hilt-android?hl=ja#generated-components

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.

🟩

🟡

.