Hiltを使ってViewModelに依存性を注入(DI)する
最終更新日:2022-05-09
本記事はJetpack Composeを使うアプリにおいて、Hiltを使ってViiewModelに依存性を注入(DI)する方法を解説します。Viewシステム(レイアウトXML/フラグメント)の場合は使用するライブラリ等が一部異なるので注意してください。
公式ドキュメントはこちらです。本記事執筆時はやや記事が足りないので本記事を活用してください。
ライブラリを追加する
まず、プロジェクトのルートにある build.gradle
でライブラリを追加します。
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
...
dependencies {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.36' // これを追加
}
}
次に、 app/build.gradle
にもライブラリを追加します。プラグインも追加します(kotlin-kaptはおそらくKSPに置き換えられるので、置き換え後に本記事を見た方は @mokelabまでお知らせください)。
plugins {
...
// この2つを追加
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
...
implementation "com.google.dagger:hilt-android:2.36"
kapt "com.google.dagger:hilt-android-compiler:2.36"
// Jetpack Compose用のライブラリ
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha03'
}
アプリケーションクラスを作る
Hiltを使うには @HiltAndroidApp
のついたアプリケーションクラスを作る必要があります。
@HiltAndroidApp
class ComposeApplication: Application()
AndroidManifest.xmlにも登録します。
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<application
...
android:name=".ComposeApplication">
<activity>
...
</activity>
</application>
</manifest>
コンストラクタを修正する
今回はToDoを扱う ToDoRepository
を用意し、必要とする場面では ToDoRepositoryImpl
が使われるようにしてみます。
interface ToDoRepository {
...
}
// @Inject constructorにする
class ToDoRepositoryImpl @Inject constructor(): ToDoRepository {
}
コンストラクタに @Inject constructor()
をつけます。これがないとHiltはインスタンスを作ってくれません。
モジュールを作る
Hiltでは、「このインターフェースのオブジェクトがほしいと言われたら、このインスタンスを渡してね」という対応を作ってあげる必要があります。
インターフェースと実装クラスを繋げるには、 @Binds
を使います。リポジトリオブジェクトは一つでいいので @Singleton
もつけます。
@Module
@InstallIn(SingletonComponent::class)
abstract class MainModule {
@Binds
@Singleton
abstract fun bindToDoRepository(impl: ToDoRepositoryImpl): ToDoRepository
}
すごく不思議なメソッド定義ですが、「そういうもの」として定義してください。
アクティビティの修正
アクティビティには @AndroidEntryPoint
をつけます。
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
...
}
ViewModelの修正
ゴールはViewModelに必要なオブジェクトをDIすることなので、ViewModelも修正します。 @HiltViewModel
をつけ、コンストラクタは @Inject constructor()
にします。そして必要となるオブジェクトをコンストラクタの引数として受け取ります。
@HiltViewModel
class MainViewModel @Inject constructor(
private val repo: ToDoRepository
): ViewModel() {
...
}
NavHostの修正
ようやくJetpack Compose特有の話題がでてきました。ViewModelオブジェクトを取得しているNavHostの部分を修正します。
NavHost(navController = navController, startDestination = "main") {
composable("main") {
// hiltViewModel() を使う
val viewModel: MainViewModel = hiltViewModel()
MainScreen(viewModel) {
navController.navigate("second")
}
}
}
これで、ViewModelが必要とするオブジェクトをHiltが注入した上でViewModelオブジェクトを作ってくれるようになります。