Dagger / Hilt:DIとその応用とは

著者: IT Sectr 公開日: 2026-05-03 読了時間: 9 分

Daggerは、JavaおよびKotlin向けの依存性注入フレームワークで、アノテーション処理を通じてコンパイル時にDIコードを生成します。Hiltは、Android向けにDaggerをラップし、コンポーネントのセットアップとライフサイクル管理を簡素化します。Google、2025年のデータによると、HiltはGoogle Play Top-100のAndroidアプリの70%以上で使用されており、事前定義されたコンポーネントを通じてActivity、Fragment、ViewModel、Serviceをサポートしています。両フレームワークはコンパイル時の依存関係グラフ検証を提供し、ランタイムの注入エラーを排除します。

重要なポイント

  • Dagger — @Module、@Provides、@Componentアノテーションによるコード生成を備えたコンパイル時DIフレームワーク
  • Hilt — @HiltAndroidApp、@AndroidEntryPoint、@HiltViewModelを通じてDaggerを簡素化するAndroidラッパー
  • Component — プロキシメソッドを通じてModuleとInjectターゲットを接続する依存関係グラフ
  • Scope — @Singleton、@ViewModelScoped、@ActivityScopedが注入されたオブジェクトのライフタイムを管理
  • Hiltは@InstallInによる依存関係グラフの分離でマルチモジュールプロジェクトをサポート

Dagger / Hiltとは?

Daggerは、コンパイル時のコード生成を備えた依存性注入フレームワークです。Squareで開発され、後にGoogleに移管されたDaggerは、Java APTアノテーションプロセッサを使用して依存関係グラフを分析し、ファクトリクラスを生成します。ランタイムDI(Guice、Koin)とは異なり、Daggerはリフレクションを使用しません。すべてのコードはコンパイル時に作成され、ランタイムの最大パフォーマンスとビルド時のエラー検出を保証します。

Hiltは、Daggerの上に構築されたGoogleのライブラリで、Android向けに最適化されています。Hiltは、Androidコンポーネントのライフサイクルに対応する事前定義されたコンポーネントを提供します:Application用の@SingletonComponent、Activity用の@ActivityComponent、Fragment用の@FragmentComponent、ViewModel用の@ViewModelComponent。これにより、純粋なDaggerで必要だった定型的なComponentとModuleの設定が不要になります。Hiltはまた、@AndroidEntryPointを通じて各Androidコンポーネントの依存関係グラフを自動的に生成します。

Google I/O 2024によると、HiltはKotlinで書かれたAndroidアプリケーションにおけるDIの推奨ソリューションです。Jetpackライブラリ(Navigation、Room、WorkManager)は、@HiltViewModelと@HiltWorkerを通じてHiltとの組み込み統合を備えています。Androidを使用しないプロジェクト(純粋なJava/Kotlinライブラリ、サーバーアプリケーション)では、Hiltラッパーなしで純粋なDaggerが使用されます。

手動依存性注入の問題

DIフレームワークがない場合、開発者はコンストラクタやファクトリを介して手動でオブジェクトを作成し、チェーンに沿って依存関係を渡します。新しい要件があるたびに、チェーン内のすべてのコンストラクタのシグネチャを変更する必要があります。Daggerはこのプロセスを自動化します:必要な型を宣言するだけで(@Inject constructor)、Daggerが依存関係グラフを作成し、すべてのネストされた型を解決します。依存関係が変更されると、Daggerは生成されたコードを自動的に更新します。チェーンで間違えることは不可能です。

依存性注入の原則

依存性注入は、オブジェクトが依存関係を自分で作成するのではなく、外部から受け取るパターンです。DIは制御の反転(IoC)の原則を実装します:クラスは自身の依存関係を作成する責任を持たず、コンストラクタ、メソッド、またはフィールドを通じてそれらを宣言します。コンストラクタインジェクションは、オブジェクトが有効な状態で作成されることを保証するため、最も推奨されます。

注入の種類Daggerの構文使用するタイミング
Constructor injection@Inject constructor主要な方法 — すべてのカスタムクラス向け
Field injection@Inject lateinit varAndroidコンポーネントのみ(Activity、Fragment)
Method injection@Inject fun bind()コンストラクト後の初期化用

コンパイル時DIの利点

DIの主な利点には、テスト容易性(依存関係をモックオブジェクトで置き換え可能)、疎結合(クラスは実装ではなくインターフェースに依存)、およびスコープによるオブジェクトのライフサイクルの明示的な管理が含まれます。Daggerは、オブジェクトがそのスコープ内で一度だけ作成され、スコープを出ると破棄されることを自動的に保証します。

Daggerのアーキテクチャ:Component、Module、Provides

Componentは、Daggerの依存関係グラフの中心的な要素です。@Componentでアノテートされたインターフェースで、Moduleと注入ターゲットの間のブリッジを記述します。Daggerはコンパイル時にComponentの実装(例:DaggerAppComponent)を生成します。Componentは、必要な型を返す抽象メソッド、またはフィールドインジェクション用のオブジェクトを受け入れるinjectメソッドを通じて、注入に利用可能な型を決定します。

kotlin
// Module: Daggerが単独で作成できない依存関係を提供する
@Module
class NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build()
    }

    @Provides
    @Singleton
    fun provideApiService(client: OkHttpClient): ApiService {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(ApiService::class.java)
    }
}

// Component: ModuleとInjectionターゲットを接続する
@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
    fun getApiService(): ApiService
}

@Moduleは、@Providesを持つメソッドを含むクラスで、依存関係のインスタンスを返します。Moduleは、Daggerが自動的に作成できない型に使用されます:サードパーティライブラリ(OkHttp、Retrofit)、コンストラクタパラメータを持つオブジェクト、実装選択のあるインターフェース。@Bindsは、メソッドがインターフェースを返し、単一の実装を受け入れる場合の@Providesの代替です。Daggerはメソッドを呼び出さずに直接キャストを生成します。

@Scopeは、依存関係グラフ内のオブジェクトのライフタイムを定義します。@Singleton — オブジェクトはアプリケーション全体で一度だけ作成されます。@ActivityScoped — オブジェクトはActivityが生きている間、生存します。@FragmentScoped — Fragmentが生きている間。スコープがない場合、Daggerは注入のたびに新しいインスタンスを作成します。@Reusable — シングルトンである必要はないが、作成にコストがかかるオブジェクトのスコープ — Daggerはインスタンスをキャッシュする場合がありますが、保証はしません。

Android向けHilt:@HiltAndroidAppと@AndroidEntryPoint

Hiltは、事前定義されたコンポーネントと自動ベースグラフ生成により、Android向けのDagger設定を簡素化します。Applicationクラスの@HiltAndroidAppアノテーションは、Hiltコンポーネントの生成をトリガーします。このアノテーションがないと、Hiltは機能しません。Hiltを使用するAndroidアプリケーションには必須です。@HiltAndroidAppは親のSingletonComponentを作成し、アプリケーションの他のすべてのコンポーネントはこれを継承します。

kotlin
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject lateinit var apiService: ApiService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // apiServiceはonCreate呼び出し前にすでに注入されています
    }
}

@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext ctx: Context): AppDatabase {
        return Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
    }
}

@AndroidEntryPointは、Activity、Fragment、Service、BroadcastReceiver、Viewのためのアノテーションです。各タイプのHiltコンポーネントを生成します:Activityの@AndroidEntryPointは、SingletonComponentを継承するActivityComponentを作成します。子コンポーネントは自動的に親のすべての依存関係を受け取ります。@Inject lateinit varを使用したフィールドインジェクションは、@AndroidEntryPointでアノテートされたクラスでのみ利用可能です。通常のクラスではコンストラクタインジェクションが使用されます。

@InstallInは、モジュールがどのHiltコンポーネントにインストールされるかを指定します。@InstallIn(SingletonComponent::class)を持つNetworkModuleは、アプリケーション全体で利用可能です。@InstallIn(ActivityComponent::class)を持つModuleは、Activity内でのみ利用可能です。これにより依存関係グラフが分離されます:Activity固有のモジュールはFragmentやViewModelから見えず、無効な依存関係の誤った使用を防ぎます。@ApplicationContextは、アプリケーションのContextを取得するためのHiltの組み込み修飾子です。

Qualifier:@Namedとカスタム修飾子

同じインターフェースの2つの異なる実装を注入する必要がある場合、修飾子が使用されます。Hiltは、文字列識別子のための@Namedと、@Qualifierを使用したカスタムアノテーションをサポートしています。例えば、異なる文字列設定のための@Named("baseUrl")と@Named("imageBaseUrl")。カスタム修飾子はコンパイル時のチェックが可能なため、@Namedよりも推奨されます。誤った文字列名はランタイムまで検出されません。

Hilt ViewModel:@HiltViewModelと@Inject constructor

@HiltViewModelは、手動のViewModelProvider.Factoryを置き換えるアノテーションです。@HiltViewModelでアノテートされ、@Inject constructorを持つクラスは、Daggerを通じて自動的にすべての依存関係を受け取ります。Hiltは、Jetpack ViewModelProviderによって使用されるViewModelFactoryを生成します。Hiltがない場合、開発者はActivityやフラグメントから各パラメータを渡して、手動でファクトリを記述する必要があります。

kotlin
@HiltViewModel
class MainViewModel
    @Inject constructor(
        private val apiService: ApiService,
        private val database: AppDatabase
    ) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    fun loadUsers() {
        viewModelScope.launch {
            _users.value = apiService.getUsers()
        }
    }
}

// Activity内 — Hiltが自動的にViewModelを作成する
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()
}

ViewModelScopedは、ViewModelが生きている間、依存関係が生存するHiltのスコープです。同じ型の2つのViewModelが同じ@ViewModelScoped依存関係を注入する場合、それぞれが独自のインスタンスを取得します。これにより、@ViewModelScopedは@ActivityScopedと区別されます。後者では、1つのActivityがすべてのフラグメントに対して1つのインスタンスを取得します。ViewModel固有の依存関係(例:SavedStateHandle)には、@HiltViewModelと@Inject constructor(savedStateHandle: SavedStateHandle)が使用されます。

Hiltは、Hilt Extensionsライブラリを通じてassisted injectionをサポートしています。Assisted injectionを使用すると、一部の依存関係がランタイムでのみ既知の場合(例:インテントからのユーザーID)、注入時にコンストラクタにパラメータを渡すことができます。Assisted injectionには、@AssistedInjectと@Assistedパラメータの組み合わせが使用されます。Hiltは、標準的な方法で注入可能なAssistedFactoryを生成します。

Dagger vs Hilt:比較と移行

純粋なDaggerでは、Componentの手動作成、スコープの定義、各Androidコンポーネントへの注入設定が必要です。開発者はAppComponent、ActivityComponent、FragmentComponentを作成し、@Subcomponentを通じてそれらの関係を管理します。このアプローチは最大の制御を提供しますが、かなりのボイラープレートコードが必要です。Daggerは、非標準のDIアーキテクチャが必要な大規模プロジェクトや、非AndroidのJava/Kotlinプロジェクトで使用されます。

Hiltはボイラープレートを自動化します:1つの@HiltAndroidApp、各コンポーネントに1つの@AndroidEntryPoint、事前定義されたスコープ。Googleはすべての新しいAndroidプロジェクトにHiltを推奨しています。DaggerからHiltへの移行には、Componentを@InstallInに置き換え、@Subcomponentを事前定義されたHiltコンポーネントに置き換え、手動のViewModelProvider.Factoryを@HiltViewModelに置き換えることが含まれます。ほとんどの@Moduleクラスは、@Providesメソッドを変更せずに@InstallInを追加して移行されます。

特性DaggerHilt
セットアップ手動:Component、Subcomponent、Builder自動:@HiltAndroidApp、@AndroidEntryPoint
Androidコンポーネント事前定義なし12以上の組み込みコンポーネント
ViewModel手動ファクトリ@HiltViewModel + @Inject constructor
マルチモジュール@Component(dependencies)経由@InstallIn + 集約経由
複雑さ高い — 経験が必要低い — 直感的に理解可能
柔軟性最大標準(95%のシナリオをカバー)

Hiltの制限:ライブラリはAndroidのみをサポート(純粋なサーバーサイドJavaプロジェクトには不適切)、特定のコンポーネント構造を強制(オーバーライドが困難)、Jetpack Composeプロジェクトにはandroid.hilt:hilt-navigation-composeへの依存関係が必要。Composeアプリケーションの場合、HiltはComposableでhiltViewModel()を通じてアクセス可能な@HiltViewModelを提供します。Activityからの手動のViewModel提供は不要です。

よくある質問

DaggerとHiltの違いは何ですか?

Daggerは、ComponentとModuleの手動設定を必要とする基本的なコンパイル時DIフレームワークです。Hiltは、コンポーネントの作成とActivity、Fragment、ViewModel、Service、BroadcastReceiverのライフサイクルとの統合を自動化するAndroidラッパーです。

@HiltAndroidAppが必要な理由は?

@HiltAndroidAppは、ApplicationのHiltコンポーネント生成を有効にします。このアノテーションがないと、HiltはすべてのActivityComponent、FragmentComponent、ViewModelComponentが継承するベースのSingletonComponentを作成できません。このアノテーションはHiltプロジェクトには必須です。

HiltはJetpack Navigationとどのように連携しますか?

Hilt Navigationは、NavBackStackEntry内のViewModelに@HiltViewModelを提供し、ナビゲーショングラフ内でViewModelをスコープするためのhiltNavGraphViewModels()を提供します。android.hilt:hilt-navigation-fragmentライブラリは、各NavBackStackEntryに対して自動的にViewModelを作成します。

HiltでContextを注入するには?

アプリケーションコンテキストには@ApplicationContext、Activityコンテキストには@ActivityContextを使用します。Hiltはこれらの修飾子をandroid.hilt:hilt-androidライブラリに組み込みで提供しています。@ActivityContextは、ActivityComponentにインストールされたモジュールでのみ利用可能です。

@Bindsとは何ですか?いつ使用しますか?

@Bindsは、メソッドが1つのパラメータのみを受け入れ、その型をインターフェースとして返す場合の@Providesの効率的な代替です。@Bindsはメソッドを呼び出さずに直接キャストを生成し、生成されるコード量を減らし、注入パフォーマンスを向上させます。

まとめ

  • Dagger — @Module、@Provides、@ComponentアノテーションとAPTによるコード生成を備えたコンパイル時DIフレームワーク
  • Hilt — @HiltAndroidApp、@AndroidEntryPoint、@InstallInと事前定義コンポーネントを備えたDagger上のAndroidラッパー
  • Componentは依存関係グラフを管理、Moduleはサードパーティクラスを提供、Providesはオブジェクトファクトリを提供
  • Scope(@Singleton、@ViewModelScoped、@ActivityScoped)はDaggerグラフ内のオブジェクトのライフタイムを定義
  • @HiltViewModelはViewModelの作成を自動化し、手動のViewModelProvider.Factoryを不要に
  • @InstallInはコンポーネントごとにモジュールを分離し、アプリケーション層間の依存関係リークを防止
  • HiltはGoogleがすべての新しいAndroidプロジェクトに推奨、Daggerは非AndroidおよびカスタムDIアーキテクチャ向け

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください