シンプルなものにして整理、定型化しておきたい。
「どこに」←「どれを」インジェクトしてるか、とそれの記述をパターン化。
💉 View ← ViewModel
MVVM でいうところの View であるこれらに、ViewModel をインジェクトする場合。
コンストラクタからインジェクトできないいわゆる「Androidクラス」。
以下のものがこれに当てはまる。
- Activity
- Fragment
- View
- Service
- BroadcastReceiver
それぞれのクラスに付ける2つのアノテーションと ktx による記述でプロパティにインジェクトする。
@AndroidEntryPoint
class MainFragment : Fragment() {
private val viewModel: MainViewModel by viewModels()
@HiltViewModel
class MainViewModel @Inject constructor(
repository: MainRepository
) : ViewModel() {
💉 ViewModel ← Repository
Module を使った ViewModel コンストラクタへのインジェクト。
インターフェースの型でリンクする。
@HiltViewModel
class MainViewModel @Inject constructor(
repository: MainRepository
) : ViewModel() {
Hilt 備え付けの Component でライフサイクルを考慮して、実装型でインジェクトする。
abstract、@Binds は、インターフェースを介す場合の、 @InstallIn( ViewModelComponent::class)、 @ViewModelScoped は、 ViewModel に対してのインジェクトのパターン。
@Module
@InstallIn(ViewModelComponent::class)
abstract class RepositoryModule {
@Binds
@ViewModelScoped
abstract fun bindRepository(impl: DefaultRepository): MainRepository
}
💉 Repository ← DataSource
Androidクラスのようなプロパティへのインジェクトとは違う @Module、@Provides を使ったコンストラクタインジェクトの Hilt基本的パターン。
SingletonComponent は、旧 ApplcationComponent で生存期間最長。
class DefaultRepository @Inject constructor(
private val localDao: LocalDao,
private val contentResolver: ContentResolver,
private val sharedPreferences: SharedPreferences
) : MainRepository {
@Module
@InstallIn(SingletonComponent::class)
object DataSourceModule {
@Provides
@Singleton
fun provideLocalDao(
@ApplicationContext context: Context
): LocalDao {
return LocalDao(context)
}
💉 DataSource ← Context
Context は、 @ApplicationContext と @ActivityContext がすでに用意されているので区別しながらアノテーション1つのみで注入できる。これ便利。
@Module
@InstallIn(SingletonComponent::class)
object DataSourceModule {
@Provides
@Singleton
fun provideLocalDao(
@ApplicationContext context: Context
): LocalDao {
return LocalDao(context)
}
しかし、どの位置でも自在にインジェクトしようとするとコケる。
[Dagger/MissingBinding] @dagger.hilt.android.qualifiers.ActivityContext android.content.Context cannot be provided without an @Provides-annotated method.
@Module 直下の @Provides メソッドの引数で使うものですか。
build.gradle
buildscript {
ext.versions = [
"hilt" : "2.41",
]
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:${versions.hilt}"
}
}
apply plugin: "org.jetbrains.kotlin.kapt"
apply plugin: "dagger.hilt.android.plugin"
dependencies {
implementation "com.google.dagger:hilt-android:${versions.hilt}"
kapt "com.google.dagger:hilt-android-compiler:${versions.hilt}"
androidTestImplementation "com.google.dagger:hilt-android-testing:${versions.hilt}"
kaptAndroidTest "com.google.dagger:hilt-compiler:${versions.hilt}"
testImplementation "com.google.dagger:hilt-android-testing:${versions.hilt}"
kaptTest "com.google.dagger:hilt-compiler:${versions.hilt}"
}
👉 Gradle Build Setup
💉 まとめ
多少の流儀はあるけど、 Component 記述がなくなっただけでも記述量は大幅に削減できます。
alpha らしく、まだコンポーネント名など変わるんかもしれんが、ここも適宜修正させてもらいます。
👉 【MVVM】 Kotlin Flow で使える5つの利用パターン
👉 skydoves/Pokedex: 🗡️ Android Pokedex using Hilt, Motion, Coroutines, Flow, Jetpack (Room, ViewModel) based on MVVM architecture.
👉 Hilt and Dagger annotations cheat sheet - Android Developers - Medium