1. daichidaiji
Changes in body
Source | HTML | Preview

はじめに

新卒で入社してから2ヶ月が立ちました。Androidに少しはなれてきたかな…
ということで今回は、Daggerを使ってDIしていきたいと思います!
Dagger Hiltが、最近話題になってますね。
今後、Daggerにしろ、KoinにしろDIコンテナを使うことは必須だと思うので復習していきます!

前回の続きです

Gradle

gradleにDaggerを追加します。

build.gradle
    // Dagger
    def daggerVersion = '2.26'
    implementation "com.google.dagger:dagger:$daggerVersion"
    kapt "com.google.dagger:dagger-compiler:$daggerVersion

実装

Daggerのオブジェクトグラフに、あるクラスのインスタンスを取得する方法を追加する必要があります。
@Injectアノテーションをつけることで、これを実現します。

今回、
ViewModelはRepositoryに依存していて、
RepositoryはDatabaseに依存しています。

ViewModel

MainViewModel.kt
class MainViewModel @Inject constructor(private val repository: TodoRepository) : ViewModel() {
    val todoList = repository.allTodoList

    fun insert(todo: Todo) = viewModelScope.launch {
        withContext(Dispatchers.IO){
            repository.insert(todo)
        }
    }

Repository

TodoRepository.kt
class TodoRepository @Inject constructor(private val todoDao: TodoDao) {
    val allTodoList = todoDao.getAll()

    @WorkerThread
    suspend fun insert(todo: Todo) {
        todoDao.insert(todo)
    }
}

@Injectアノテーションをつけることで、クラスのインスタンスの提供方法をDaggerに伝えることができました。

Module

Interfaceなど、直接インスタンス化できないものの提供方法をDaggerに伝える必要があります。
依存関係の定義をグループ化するには、@Modelu@Moduleアノテーションをつけます。

DatabaseModule.kt
@Module
class DatabaseModule() {

    @Singleton
    @Provides
    fun provideTodoDatabase(context: Context) =
        Room.databaseBuilder(context,
            TodoDatabase::class.java,
            "database_name")
            .build()

    @Singleton
    @Provides
    fun provideTodoDao(todoDatabase: TodoDatabase) = todoDatabase.todoDao()
}

@Moduleアノテーションをつけたクラスに、インスタンスの生成方法を@Providesアノテーションで実現します。

Component

ComponentはDaggerがオブジェクトグラフを生成して、あるクラスのインスタンスを取得する役割があります。
Componentを実装するには、@ComponentアノテーションをInterfaceにつけます。

AppComponent.kt
@Singleton
@Component(
    modules = [
        DatabaseModule::class
    ]
)
Interface AppComponent {

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance context: Context): AppComponent
    }
    fun mainViewModel(): MainViewModel
}

@Singletonアノテーションをつけることで、Singletonを実現します。
また、ComponentにModuleクラスで定義した依存関係を追加するために、modelumodulesパラメータに記述します。

@Component.Factoryアノテーションでは、Componentのインスタンス取得時に特別な処理を定義することができます。今回は、Daggerのオブジェクトグラフ生成時にContextを渡したいので、引数に@BindsInstanceアノテーションをつけます。

Aplication

AppComponent.Factoryを使い、AppComponentのインスタンスを取得します。
インスタンス取得時にContextを渡します。

TodoAplication.kt
class TodoApplication : Application() {
    companion object {
        lateinit var component: AppComponent private set
    }

    override fun onCreate() {
        super.onCreate()
        component = DaggerAppComponent.factory().create(applicationContext)
    }
}

Activity

MainActivity.kt
class MainActivity : AppCompatActivity() {

    private val mainViewModel: MainViewModel by lazy {
        TodoApplication.component.mainViewModel()
    }
...

これでMainActivityにMainViewModelを提供することができました。

おわりに

Todoアプリの削除機能を実装する前に、Daggerを使ってDIしてみました!
もし間違った記述があるなら、コメントで教えてほしいです!!!

Dagger Hiltも、時間があったら触ってみようかな。