Dagger 2からHiltへの移行
はじめに
Android開発では、アプリケーションの構造を整え、依存性の管理を簡素化するためにMVVM(Model-View-ViewModel)パターンとDI(Dependency Injection)パターンが広く使われています。今回は、Jetpack Composeを使用してこれらのパターンを導入する方法を紹介し、特にDagger 2からHiltへの移行手順に焦点を当てます。
Dagger 2を使用した設定
まず、Dagger 2を使用した場合の設定を示します。
必要なライブラリ
build.gradle
ファイルに以下の依存関係を追加します。
dependencies {
// Jetpack Compose
implementation "androidx.compose.ui:ui:1.0.0"
implementation "androidx.compose.material:material:1.0.0"
implementation "androidx.compose.ui:ui-tooling-preview:1.0.0"
implementation "androidx.activity:activity-compose:1.3.0"
// Dagger 2
implementation 'com.google.dagger:dagger:2.37'
kapt 'com.google.dagger:dagger-compiler:2.37'
// Lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
}
Dagger 2設定
@Module
object AppModule {
@Provides
@Singleton
fun provideRepository(): Repository {
return RepositoryImpl()
}
@Provides
fun provideViewModelFactory(repository: Repository): ViewModelProvider.Factory {
return ViewModelFactory(repository)
}
}
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
@Component.Factory
interface Factory {
fun create(@BindsInstance application: Application): AppComponent
}
}
class MyApplication : Application() {
val appComponent: AppComponent by lazy {
DaggerAppComponent.factory().create(this)
}
}
interface Repository {
suspend fun fetchData(): List
}
RepositoryImpl.kt
class RepositoryImpl : Repository {
override suspend fun fetchData(): List {
return listOf("Data 1", "Data 2", "Data 3")
}
}
class MyViewModel(private val repository: Repository) : ViewModel() {
private val _data = MutableLiveData>()
val data: LiveData> get() = _data
init {
viewModelScope.launch {
_data.value = repository.fetchData()
}
}
}
class ViewModelFactory(private val repository: Repository) : ViewModelProvider.Factory {
override fun create(modelClass: Class): T {
if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return MyViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
class MainActivity : ComponentActivity() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel: MyViewModel by viewModels { viewModelFactory }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(applicationContext as MyApplication).appComponent.inject(this)
setContent {
MyApp(viewModel)
}
}
}
@Composable
fun MyApp(viewModel: MyViewModel) {
val data by viewModel.data.observeAsState(initial = emptyList())
MaterialTheme {
Column(
modifier = Modifier.padding(16.dp)
) {
data.forEach { item ->
Text(text = item, style = MaterialTheme.typography.h5)
}
}
}
}
Hiltへの移行
Hiltに置き換える手順を以下に示します。HiltはDagger 2の上に構築されており、Android開発向けに最適化されています。設定が簡単で、注入ポイントを簡潔に定義できます。
必要なライブラリ
build.gradle
ファイルにHiltの依存関係を追加します。
dependencies {
// Jetpack Compose
implementation "androidx.compose.ui:ui:1.0.0"
implementation "androidx.compose.material:material:1.0.0"
implementation "androidx.compose.ui:ui-tooling-preview:1.0.0"
implementation "androidx.activity:activity-compose:1.3.0"
// Hilt
implementation "com.google.dagger:hilt-android:2.37"
kapt "com.google.dagger:hilt-android-compiler:2.37"
// Lifecycle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07"
// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0"
}
プロジェクトのトップレベルのbuild.gradle
ファイルに以下を追加し、アプリレベルのbuild.gradle
ファイルにHiltプラグインを適用します。
buildscript {
dependencies {
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.37'
}
}
apply plugin: 'dagger.hilt.android.plugin'
Hiltの設定
MyApplication.kt
ファイルを以下のように変更します。
@HiltAndroidApp
class MyApplication : Application()
MainActivity.kt
ファイルを以下のように変更します。
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val viewModel: MyViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp(viewModel)
}
}
}
Hiltのモジュールとして設定するために、AppModule.kt
ファイルを以下のように変更します。
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideRepository(): Repository {
return RepositoryImpl()
}
}
Hiltを使用してViewModelの依存関係を注入するために、以下のように変更します。
@HiltViewModel
class MyViewModel @Inject constructor(
private val repository: Repository
) : ViewModel() {
private val _data = MutableLiveData>()
val data: LiveData> get() = _data
init {
viewModelScope.launch {
_data.value = repository.fetchData()
}
}
}
UIのコードは変更しなくても大丈夫です。
@Composable
fun MyApp(viewModel: MyViewModel) {
val data by viewModel.data.observeAsState(initial = emptyList())
MaterialTheme {
Column(
modifier = Modifier.padding(16.dp)
) {
data.forEach { item ->
Text(text = item, style = MaterialTheme.typography.h5)
}
}
}
}
AppComponent.ktは不要のため削除してください。
まとめ
Dagger 2を使用した場合、設定が複雑で学習コストが高いことがありますが、Hiltを使用することで、これらの設定を簡略化し、Android開発における依存性注入をよりシンプルに行うことができます。今回紹介した方法を参考にして、Jetpack ComposeとMVVMパターン、リポジトリパターンを組み合わせた効率的なアプリケーション開発を進めてみてください。
参考文献
多角的な反証
Hiltの制限: 特定のケースではカスタマイズが難しい場合があります。
MVVMの学習コスト: 既存のアーキテクチャからの移行に時間がかかる可能性があります。
このブログ記事を通じて、Jetpack Composeを活用したMVVMパターンとDIパターンの導入方法が理解できることを願っています。
Discussion