0
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ってなんだ

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. MyApplicationを作成し、@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の基本的な使い所4つ

  1. ViewModelとCompose(Fragment/Activity)のDI

    • @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: StateFlow<HomeScreenViewState> = _viewState.asStateFlow()
    
        init {
            loadCharacters()
        }
    
        private fun loadCharacters() {
            viewModelScope.launch {
                try {
                    val pagingSource = repository.getCharacterPagingSource()
                    _viewState.update {
                        HomeScreenViewState.GridDisplay(
                            characters = Pager(
                                config = PagingConfig(enablePlaceholders = false, pageSize = 20),
                                pagingSourceFactory = { pagingSource }
                            ).flow.cachedIn(viewModelScope)
                        )
                    }
                } catch (e: Exception) {
                    _viewState.update { HomeScreenViewState.Error(e.message ?: "Unknown error") }
                }
            }
        }
    }
    
  3. RepositoryインターフェイスとRepositoryImpl(実装クラス)のDI (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 {
        return HttpClient(OkHttp) {
            defaultRequest { url(BASE_URL) }

            install(Logging) {
                logger = Logger.SIMPLE
            }

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

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

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

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

使ってみたメリット

  1. NavHostがスッキリ書けるため嬉しい
@Composable
fun MyAppNavHost(
    navController: NavHostController = rememberNavController(),
) {
    NavHost(
        navController = navController,
        startDestination = HomeScreenRoute
    ) {
        homeScreen()
        characterDetailsScreen()
        characterEpisodeScreen()
    }
}
  1. ボイラープレートコードを記述しなくて良い
    • ViewModel Factoryとかその辺を書かなくて良くなる

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

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 {
        return OkHttpClient.Builder()
            // ... OkHttpClientの設定 ...
            .build()
    }

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { // OkHttpClientはHiltが自動的に注入
        return 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 アノテーションを付け、戻り値の型をインターフェース、引数の型を実装クラスにs
    • 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 アノテーションが付いた抽象クラス内で使用。

参考文献

Hilt を使用した依存関係挿入  |  Android Developers

0
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
0
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?