1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Hiltってなんだ

Last updated at Posted at 2025-04-05

Hiltってなんだ

対象読者

  • Kotlinの基本文法がわかる人
  • DIの概念をなんとなく知っている人
  • Hiltを初めて使う or 使い始めたばかりの人

Hiltとは

  • DIをするためのライブラリ
  • DIについての説明は省略
  • DI ≠ DIP(似てるけど別物)
    • DI(Dependency Injection): 依存性を外から注入する「仕組み・手法」
    • DIP(Dependency Inversion Principle): 依存性逆転の原則。「具体ではなく抽象に依存すべき」という「設計の考え方」
    • HiltはDIを行うライブラリだが、DIPの実現にも役立つ

用語の整理

同じ意味でも複数の言い方があるので整理:

日本語 英語 / カタカナ 意味
依存 / 依存性 / 依存関係 Dependency あるクラスが動くために必要なオブジェクト
依存性の注入 DI / Dependency Injection 外から依存を渡す仕組み
依存性逆転の原則 DIP / Dependency Inversion Principle 具体ではなく抽象に依存する設計原則
注入する Inject / インジェクト 依存を渡す動作
コンストラクタ注入 Constructor Injection / コンストラクタインジェクション コンストラクタ引数で依存を渡す方法(推奨)
フィールド注入 Field Injection / フィールドインジェクション フィールドに直接依存を渡す方法
スコープ Scope インスタンスの生存期間
バインド Bind インターフェースと実装を紐付ける
プロバイド Provide 依存オブジェクトを提供する

AndroidのDIライブラリ

ライブラリ 特徴
Hilt Google公式。Daggerベース。KSPでコード生成。Androidに特化
Koin 軽量でシンプル。KMP対応。ランタイムで依存解決(コード生成なし)
Metro 新しめ。KMP対応。DroidKaigi 2025で採用
  • Android専用プロジェクト → Hiltが推奨(Google公式)
  • KMP(Kotlin Multiplatform) → Koinがよく使われる(iOSでも動く)

このドキュメントでは、Android開発で最も使われているHiltについて解説します。

Hiltがない世界の辛さ

Hiltを使わずに手動でオブジェクトを生成・受け渡しするとどうなるか見てみましょう。

手動DIのコード例

// Hiltなしの世界

// 1. Application で ApiClient を作る
class MyApplication : Application() {
    val apiClient = ApiClient()  // ここで生成
}

// 2. Activity で Repository を作る
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Applicationからapiを取得して...
        val app = application as MyApplication
        val repository = UserRepository(app.apiClient)

        // ViewModelはどうする?
        val viewModel = UserViewModel(repository) // これはNG!

        setContent {
            UserScreen(viewModel)  // 渡すしかない
        }
    }
}

問題1: ViewModelを直接newできない

ViewModelはViewModelProvider経由でないとライフサイクル管理されない。画面回転で状態が消える。

// じゃあViewModelProvider使おう...でも引数付きViewModelは?

class UserViewModel(
    private val repository: UserRepository  // これを渡したい
) : ViewModel()

// Factory を自分で書く必要がある!
class UserViewModelFactory(
    private val repository: UserRepository
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return UserViewModel(repository) as T
    }
}

// Activity内で使う
val factory = UserViewModelFactory(repository)
val viewModel = ViewModelProvider(this, factory)[UserViewModel::class.java]

問題2: ViewModelごとにFactoryを書く地獄

10個のViewModelがあれば10個のFactory。引数が変わるたびに修正が必要。

問題3: 依存の連鎖が爆発する

ViewModel → UseCase → Repository → ApiClient → OkHttp → ...

全部手動で繋ぐとActivityが肥大化して管理不能に。

問題4: テストできない

val apiClient = ApiClient()がハードコードされていたら、テスト時にモックに差し替えられない。

手動DI vs Hilt 比較表

手動DIの問題 Hiltを使うと
Activity/Applicationが依存解決の責務を持つ 「欲しいものを宣言」すれば勝手に届く
ViewModelのライフサイクル管理が複雑 ライフサイクルは自動管理
テスト時の差し替えが困難 簡単に差し替え可能
依存が増えるたびにボイラープレート増殖 依存が増えてもコード変更最小限

Hiltの導入方法(ざっくり)

  1. 依存関係の追加

    • kaptではなくkspにすると良い(ビルドが早くなるため)
  2. MainActivity@AndroidEntryPointアノテーションを付ける

    @AndroidEntryPoint
    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            enableEdgeToEdge()
            setContent {
                Multi_module_test_theme {
                    val navController = rememberNavController()
                    CompositionLocalProvider(LocalNavController provides navController) {
                        MyAppNavHost(navController)
                    }
                }
            }
        }
    }
    
  3. MainApplication.ktを作成し、@HiltAndroidApp というアノテーションを付ける

    @HiltAndroidApp
    class MainApplication: Application()
    
  4. Androidマニフェストにそのことを記述する

    <?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: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.Multi_module_test"
             <!-- 以下を追加 -->
            android:name=".MainApplication"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true"
                android:label="@string/app_name"
                android:theme="@style/Theme.Multi_module_test">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

インジェクションの種類

Hiltでは2種類のインジェクション方法があります。

コンストラクタインジェクション(推奨)

クラスのコンストラクタに@Injectを付けて依存を受け取る方法。こちらが推奨。

// ViewModel、RepositoryImpl、UseCaseなど、自分でインスタンス化できるクラス
class UserRepositoryImpl @Inject constructor(
    private val apiClient: ApiClient,
    private val database: AppDatabase,
) : UserRepository {
    // ...
}

@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository,  // インターフェースに依存
) : ViewModel() {
    // ...
}

メリット:

  • 依存関係が明確(コンストラクタを見ればわかる)
  • イミュータブル(valで受け取れる)
  • テストしやすい(コンストラクタに直接モックを渡せる)
  • 使用側が依存を気にしなくて済む
    • 例:ComposableはViewModelの依存(Repository等)を知らなくていい
    • hiltViewModel()を呼ぶだけで、Hiltが裏で全部解決してくれる

フィールドインジェクション(仕方ない場合のみ)

フィールドに@Injectを付けて依存を受け取る方法。Androidフレームワークがインスタンスを生成するクラスでのみ使用。

// Activity、Fragment、Serviceなど、Androidが生成するクラス
@AndroidEntryPoint
class MainActivity : ComponentActivity() {

    @Inject
    lateinit var analyticsHelper: AnalyticsHelper  // フィールドインジェクション

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // この時点でanalyticsHelperは注入済み
    }
}

なぜフィールドインジェクションが必要?

  • Activity/Fragmentのコンストラクタはシステムが呼び出すため、引数を追加できない
  • @AndroidEntryPointを付けると、HiltがonCreate()の前に自動でフィールドに注入してくれる

注意点:

  • lateinit varにする必要がある(イミュータブルにできない)
  • privateにできない(Hiltがアクセスする必要があるため)

どちらを使うべき?

ケース 使うべき方法
ViewModel、Repository、UseCase コンストラクタインジェクション
Activity、Fragment、Service フィールドインジェクション
自分でnewできるクラス コンストラクタインジェクション
Androidがnewするクラス フィールドインジェクション

Hiltの基本的な使い所4つ

  1. ComposeでのViewModel利用

    • @HiltViewModel アノテーションをViewModelにつける
    • ComposeではコンストラクタにviewModel: HogeViewModel = hiltViewModel() を入れる
      • hilt-navigation-compose
    • 依存関係を自動で注入してくれる
    @Composable
    fun HomeScreen(
        viewModel: HomeScreenViewModel = hiltViewModel(),
    ) {
        val viewState by viewModel.viewState.collectAsStateWithLifecycle()
    
        when (val state = viewState) {
            is HomeScreenViewState.Loading -> {
                LoadingState()
            }
    
            is HomeScreenViewState.GridDisplay -> {
                Log.d("HomeScreen", "viewState in GridDisplay: $state")
                val characters = state.characters.collectAsLazyPagingItems()
                CharacterGrid(characters = characters)
            }
    
            is HomeScreenViewState.Error -> {
                val errorMessage = state.errorMessage
                Text(text = "Error: $errorMessage")
            }
        }
    }
    
    @HiltViewModel
    class HomeScreenViewModel @Inject constructor(): ViewModel() {}
    
  2. ViewModelとRepository(UseCase)のDI

    • ViewModelのコンストラクタに @Inject アノテーションを付ける
    • Repository(UseCase)をコンストラクタ引数として渡す
    • HiltがRepositoryのインスタンスを生成し、ViewModelに注入
    @HiltViewModel
    class HomeScreenViewModel @Inject constructor(
        private val repository: CharacterRepository,
    ) : ViewModel() {
        private val _viewState = MutableStateFlow<HomeScreenViewState>(HomeScreenViewState.Loading)
        val viewState = _viewState.asStateFlow()
    
        init {
            loadCharacters()
        }
    
        private fun loadCharacters() = viewModelScope.launch {
            repository.getCharacterPagingSource().fold(
                onSuccess = { pagingSource ->
                    _viewState.value = HomeScreenViewState.GridDisplay(
                        characters = Pager(
                            config = PagingConfig(enablePlaceholders = false, pageSize = 20),
                            pagingSourceFactory = { pagingSource }
                        ).flow.cachedIn(viewModelScope)
                    )
                },
                onFailure = { e ->
                    _viewState.value = HomeScreenViewState.Error(e.message ?: "Unknown error")
                }
            )
        }
    }
    
  3. RepositoryとRepositoryImplのDI (RepositoryModule)

    DIP(依存性逆転の原則)とは

    一言で言うと: 「具体的なもの」ではなく「約束(インターフェース)」に頼ろう、という考え方。

    身近な例で説明:
    スマホの充電を想像してください。USB Type-Cという「規格」に従っていれば、
    どのメーカーのケーブルでも充電できます。もしスマホが「特定メーカーのケーブル専用」だったら、
    そのケーブルが壊れたら終わりです。

    • 悪い例(DIPに違反): ViewModelがRepositoryImpl(具体的な実装)に直接依存
      → RepositoryImplを変えるたびにViewModelも修正が必要
    • 良い例(DIPに準拠): ViewModelがRepository(インターフェース)に依存
      → 実装を差し替えてもViewModelは変更不要。テスト用のモック実装も簡単に注入できる

    Hiltでの実現: @Bindsを使ってインターフェースと実装クラスを紐付ける

    Repository + RepositoryImpl + RepositoryModuleの関係性

    ❌ 悪い例(DIPに違反)

    ✅ 良い例(DIPに準拠)

    コンポーネント 役割 例えるなら
    Repository 「何ができるか」を定義するインターフェース USB Type-Cという規格
    RepositoryImpl 実際の処理を行う実装クラス 実際のType-Cケーブル
    RepositoryModule HiltにRepositoryとRepositoryImplの紐付けを教える 「このスマホにはこのケーブルを使う」という設定

    実装例

    1. Repository(インターフェース定義)

    interface CharacterRepository {
        suspend fun getCharacterPagingSource(): Result<CharacterPagingSource>
        suspend fun getCharacterById(id: Int): Result<Character>
    }
    

    2. RepositoryImpl(実装クラス)

    class CharacterRepositoryImpl @Inject constructor(
        private val ktorClient: KtorClient,
    ) : CharacterRepository {
    
        override suspend fun getCharacterPagingSource(): Result<CharacterPagingSource> =
            runCatching { CharacterPagingSource(ktorClient) }
    
        override suspend fun getCharacterById(id: Int): Result<Character> =
            runCatching { ktorClient.getCharacter(id) }
    }
    

    3. RepositoryModule(バインディング)

    • @Binds アノテーションを使用
    • @Module@InstallIn アノテーションが付いた抽象クラス内で、@Binds アノテーションを付けた抽象メソッドを定義
    • メソッドの引数で実装クラスを指定し、戻り値でインターフェースを指定
    • @Provides よりも @Binds の方が効率的(インスタンス生成が不要なため)
    • bindHoge という名前をつけがち
    @Module
    @InstallIn(SingletonComponent::class)
    abstract class RepositoryModule {
    
        @Singleton
        @Binds
        abstract fun bindRepository(
            repositoryImpl: CharacterRepositoryImpl,
        ): CharacterRepository
    }
    
  4. APIClient(RetrofitKtorなど)のDI (NetworkModule)

    • @Provides アノテーションを使用。
    • @Module@InstallIn アノテーションが付いたクラス内で、@Provides アノテーションを付けたメソッドを定義。
    • メソッド内でAPIClientのインスタンスを生成して返す。
    • 必要に応じて、OkHttpClientなどの設定も行う。
    • Singletonスコープにすることが多い。
    • provideHogeという名前をつけがち
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideHttpClient(): HttpClient =
        HttpClient(OkHttp) {
            defaultRequest { url(BASE_URL) }

            install(Logging) {
                logger = Logger.SIMPLE
            }

            install(ContentNegotiation) {
                json(Json {
                    ignoreUnknownKeys = true
                })
            }
        }

    @Provides
    @Singleton
    fun provideKtorClient(httpClient: HttpClient): KtorClient =
        KtorClient(httpClient)
}

// Retrofitの例
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient =
        OkHttpClient.Builder()
            // ... OkHttpClientの設定 ...
        .build()
    

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =
        Retrofit.Builder()
            .baseUrl("https://example.com/")
            .client(okHttpClient) // Dagger/HiltがOkHttpClientを自動的に注入
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    
}

使ってみたメリット

  1. ボイラープレートコードを書かなくて良い

    • ViewModel Factoryを自分で書く必要がない
    • hiltViewModel() を呼ぶだけでViewModelが取得できる
  2. 依存の変更に強い

    • ViewModelの依存が増えても、呼び出し側のコードは変更不要
    • 例:UserViewModelに新しい依存を追加しても、Compose側はhiltViewModel()のまま
  3. テストしやすい

    • インターフェースに依存しているので、テスト用のFake実装に差し替えやすい
  4. Navigation Composeとの相性が良い

    • Nav2(Navigation Compose)でもNav3でも、画面ごとにViewModelを簡単に取得できる
    • 各画面でhiltViewModel()を呼ぶだけ

スコープ管理

スコープとは?

スコープ = インスタンスの生存期間

同じスコープ内では同じインスタンスが再利用される。スコープが違えば別のインスタンスが生成される。

Hiltの主要なスコープ

スコープ Component 生存期間 主な用途
@Singleton SingletonComponent アプリ全体 APIクライアント、DB
@ActivityScoped ActivityComponent Activity Activity固有の状態
@ViewModelScoped ViewModelComponent ViewModel ViewModel内で共有したい依存
@FragmentScoped FragmentComponent Fragment Fragment固有の状態

スコープの図解

使い分けの目安

// アプリ全体で1つでいい → @Singleton
@Singleton
@Provides
fun provideRetrofit(): Retrofit = ...

// ViewModelごとに持ちたい → @ViewModelScoped
@ViewModelScoped
@Provides
fun provideUseCase(): SomeUseCase = ...

// スコープなし → 毎回新しいインスタンス
@Provides
fun provideFormatter(): DateFormatter = DateFormatter()

実務での使い分け

結論:ほとんどの場合 @Singleton で十分

スコープ 実務での使用頻度 コメント
@Singleton ⭐⭐⭐ よく使う Repository, APIクライアント, DBなど
@ViewModelScoped ⭐ たまに使う ViewModel内で状態を共有したい特殊ケース
@ActivityScoped ほぼ使わない Composeメインだと出番なし
@FragmentScoped ほぼ使わない Fragmentを使わないなら不要

@ViewModelScoped を使う場面:

// 複数のUseCaseが同じ状態を共有したい場合
@ViewModelScoped
class ScreenStateHolder @Inject constructor() {
    var currentPage = 0
}

@HiltViewModel
class MyViewModel @Inject constructor(
    private val loadUseCase: LoadUseCase,      // 同じStateHolderを参照
    private val refreshUseCase: RefreshUseCase, // 同じStateHolderを参照
) : ViewModel()

迷ったら @Singleton でOK。 問題が起きてから細かく分ければいい。

よくある間違い

// ❌ NG: ViewModelScopedなのにSingletonComponentにインストール
@Module
@InstallIn(SingletonComponent::class)  // ← ここが間違い
object MyModule {
    @ViewModelScoped  // ← SingletonComponentでは使えない
    @Provides
    fun provideUseCase(): UseCase = ...
}

// ✅ OK: ViewModelScopedはViewModelComponentに
@Module
@InstallIn(ViewModelComponent::class)  // ← 正しい
object MyModule {
    @ViewModelScoped
    @Provides
    fun provideUseCase(): UseCase = ...
}

ポイント: スコープアノテーションと@InstallInのComponentは対応させる必要がある

スコープ 対応するComponent
@Singleton SingletonComponent
@ActivityScoped ActivityComponent
@ViewModelScoped ViewModelComponent
@FragmentScoped FragmentComponent

テストでの活用

Hiltがテストを楽にする理由

DIPに従って設計されていれば、本番用の実装をテスト用に差し替えられる

本番環境:
ViewModel → Repository(インターフェース)→ RepositoryImpl → 実際のAPI

テスト環境:
ViewModel → Repository(インターフェース)→ FakeRepository → モックデータ

テスト用の設定

1. 依存関係の追加

// build.gradle.kts
dependencies {
    testImplementation("com.google.dagger:hilt-android-testing:2.x.x")
    kspTest("com.google.dagger:hilt-android-compiler:2.x.x")

    androidTestImplementation("com.google.dagger:hilt-android-testing:2.x.x")
    kspAndroidTest("com.google.dagger:hilt-android-compiler:2.x.x")
}

2. テストクラスの書き方

@HiltAndroidTest  // Hiltのテストであることを宣言
class UserViewModelTest {

    @get:Rule
    val hiltRule = HiltAndroidRule(this)  // Hiltのセットアップ

    @Inject
    lateinit var viewModel: UserViewModel

    @Before
    fun setup() {
        hiltRule.inject()  // 依存を注入
    }

    @Test
    fun `ユ取得が成功する`() {
        // viewModelを使ったテスト
    }
}

3. テスト用の実装に差し替える

// 本番用のモジュールを無効化して、テスト用に差し替え
@HiltAndroidTest
@UninstallModules(RepositoryModule::class)  // 本番用を無効化
class UserViewModelTest {

    @Module
    @InstallIn(SingletonComponent::class)
    object TestRepositoryModule {
        @Singleton
        @Provides
        fun provideRepository(): UserRepository = FakeUserRepository()
    }

    // ...
}

FakeRepositoryの例

class FakeUserRepository : UserRepository {

    private val fakeUsers = mutableListOf(
        User(id = 1, name = "テスト太郎"),
        User(id = 2, name = "テスト花子"),
    )

    override suspend fun getUsers(): List<User> = fakeUsers

    override suspend fun getUserById(id: Int): User? =
        fakeUsers.find { it.id == id }

    // テスト用にデータを操作するメソッドも追加できる
    fun addUser(user: User) {
        fakeUsers.add(user)
    }
}

テストが楽になるポイント

Hiltなし Hiltあり
手動でFakeを注入する必要がある @UninstallModulesで簡単に差し替え
依存の連鎖を全部自分で構築 Hiltが自動で解決
テストごとにセットアップが複雑 @HiltAndroidTestで統一

やってて詰まった点(アノテーションの意味と使い所)

1. @Module

  • 意味:
    • このクラスがDagger/Hiltのモジュールであることを示します。
    • モジュールは、依存オブジェクトの提供方法を定義する場所です。
    • Hiltは、@Moduleアノテーションが付いたクラスを見つけて、そこから依存関係の情報を取得します。
  • 使いどころ:
    • 依存オブジェクトの生成ロジック(@Providesメソッド)や、インターフェースと実装クラスのバインディング(@Bindsメソッド)を記述するクラスに付けます。
    • 通常、object または abstract class として定義します。
      • object: @Provides メソッドのみを含むモジュールに適しています。
      • abstract class: @Binds メソッドを含むモジュール、または @Binds@Provides を組み合わせたモジュールに適しています。

2. @Provides

  • 意味:
    • このメソッドが依存オブジェクトを提供することを示します。
    • @Provides メソッド内で、依存オブジェクトを生成し、return します。
    • メソッドの戻り値の型が、提供される依存オブジェクトの型になります。
    • メソッドの引数には、依存オブジェクトの生成に必要な他の依存オブジェクトを指定できます(Dagger/Hiltが自動的に注入します)。
  • 使いどころ:
    • 外部ライブラリのインスタンス(Retrofit, OkHttpClient, Room Databaseなど)を提供する。
    • 複雑な初期化ロジックが必要なオブジェクトを提供する。
    • インターフェースに対して複数の実装が存在し、条件によって使い分けたい場合。
    • @Module アノテーションが付いたクラス(object または class)内に記述します。

Kotlin

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

    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient =
        OkHttpClient.Builder()
            // ... OkHttpClientの設定 ...
            .build()

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =  // OkHttpClientはHiltが自動的に注入
        Retrofit.Builder()
            .baseUrl("https://example.com/")
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
}

3. @Singleton

  • 意味:
    • このアノテーションが付いた依存オブジェクトが、アプリケーション全体で1つだけ生成され、共有されることを示します(Singletonスコープ)。
    • @Provides メソッドや、@Inject が付いたコンストラクタに付けることができます。
  • 使いどころ:
    • アプリケーション全体で同じインスタンスを使い回したいオブジェクト(データベース接続、ネットワーククライアント、設定オブジェクトなど)に付けます。

4. @InstallIn(Component::class)

  • 意味 (Hilt専用):
    • @Module アノテーションと一緒に使用します。
    • このモジュールが、どのコンポーネントにインストールされるか(どのスコープで有効になるか)を指定します。
    • Component::class には、Hiltが提供する定義済みのコンポーネントクラスを指定します。
      • SingletonComponent::class: アプリケーション全体 (Singleton)
      • ActivityComponent::class: Activity
      • FragmentComponent::class: Fragment
      • ViewModelComponent::class: ViewModel
      • ServiceComponent::class: Service
      • ViewComponent::class: View (カスタムViewなど)
      • ViewWithFragmentComponent::class: Fragment内のView
    • どのコンポーネントにインストールするかによって、提供される依存オブジェクトのスコープ(生存期間)が決まります。
  • 使いどころ:
    • @Module アノテーションが付いたクラスに必ず付けます。

5. @Binds

  • 意味:
    • インターフェースとその実装クラスを関連付ける(バインドする)ために使用
    • 抽象メソッドに @Binds アノテーションを付け、戻り値の型をインターフェース、引数の型を実装クラスにする
    • Dagger/Hiltは、@Binds メソッドの情報を元に、インターフェースが要求された場合に、どの実装クラスのインスタンスを提供すればよいかを判断
    • @Binds メソッドは抽象メソッドなので、実装はDagger/Hiltが自動生成
  • 使いどころ:
    • インターフェースと実装クラスが1対1で対応する場合に使用
    • @Provides を使うよりも @Binds を使う方が効率的(インスタンス生成のオーバーヘッドがないため)
    • @Module アノテーションが付いた抽象クラス内で使用
アノテーション 役割 使いどころ
@Module このクラスがDagger/Hiltのモジュールであることを示す。依存オブジェクトの提供方法を定義する場所。 依存オブジェクトの生成ロジック(@Provides)やバインディング(@Binds)を記述するクラスに付ける。通常は object (Kotlin) または abstract class。HogeModuleみたいな名前のファイル。APIクライアントやRepositoryのDIのときに用いる
@Provides このメソッドが依存オブジェクトを提供することを示す。メソッド内で依存オブジェクトを生成して return する。 外部ライブラリのインスタンスや、複雑な初期化が必要なオブジェクトの提供、インターフェースに対して複数の実装が存在し条件によって使い分けたい場合に利用。@Module が付いたクラス内に記述。
@Singleton このアノテーションが付いた依存オブジェクトが、アプリケーション全体で1つだけ生成され、共有されることを示す(Singletonスコープ)。 アプリケーション全体で同じインスタンスを使い回したいオブジェクト(データベース接続、ネットワーククライアントなど)に付ける。@Provides メソッドや、@Inject が付いたコンストラクタに付ける。
@InstallIn(Component::class) (Hilt専用) @Moduleと一緒に使用。モジュールをどのコンポーネントにインストールするか(どのスコープで有効にするか)を指定する。SingletonComponent::class はアプリケーション全体で共有されるシングルトンコンポーネントを指定。 @Module アノテーションが付いたクラスに必ず付ける。
@Binds インターフェースとその実装クラスを関連付ける。抽象メソッドに @Binds を付け、戻り値の型をインターフェース、引数の型を実装クラスにする。Dagger/Hiltが実装クラスのインスタンスを提供。 インターフェースと実装クラスが1対1で対応する場合に使用。@Provides よりも効率的。@Module アノテーションが付いた抽象クラス内で使用。

参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?