Tatsuro のテックブログ

アプリ開発の技術情報を発信します。

【Android × Kotlin】ViewModelProvider.Factory を使って、コンストラクタ経由で ViewModel に値を渡す

今回は、ViewModelProvider.Factory を使って、コンストラクタ経由で ViewModel に値を渡す方法を解説します。

なお、ここに掲載しているソースコードは以下の環境で動作確認しています。

  • Android Studio Chipmunk | 2021.2.1 Patch 1
  • JDK 11.0.12
  • Android Gradle Plugin 7.2.1
  • Kotlin 1.7.0
  • Gradle 7.4.2
  • androidx.lifecycle 2.4.1
  • androidx.activity 1.4.0

概要

通常、ViewModel のコンストラクタに独自の引数、すなわち Application や SavedStateHandle 以外の引数を追加することはできません。

なぜなら、ViewModel の生成はその利用者が直接コンストラクタを呼ぶのではなくて、ViewModel を管理する ViewModelProvider にて ViewModel の Factory を使って行われるからです。

よって、ViewModel のコンストラクタに引数を追加して、その引数ありのコンストラクタで ViewModel を生成するためには、カスタムの Factory を作成し、そのインスタンスを ViewModelProvider に設定する必要があります。

今回は、実際に ViewModelProvider.Factory を継承して ViewModel のカスタム Factory を作成しながら、ViewModelProvider.Factory を使って、コンストラクタ経由で ViewModel に値を渡す方法を解説します。

ライブラリのインポート

ViewModel を使用するために、アプリの build.gradle ファイルの dependencieslifecycle-viewmodel-ktx を追加します。また、Activity で viewModels を使って ViewModel インスタンスを取得するため、activity-ktx も追加します。

dependencies {
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
    implementation "androidx.activity:activity-ktx:1.4.0"
}

ViewModelProvider.Factory を使って、ViewModel に値を渡す

今回は、以下のようなサービスクラスをコンストラクタ経由で ViewModel に渡すコードを作成します。

interface SampleService {
    fun getText(): String
}

class SampleServiceImpl : SampleService {
    override fun getText(): String = "Sample"
}

まず、ViewModel から作成します。

class SimpleViewModel(
    private val service: SampleService
) : ViewModel() {

    /** テキストを取得する。 */
    fun getText() = service.getText()

    class Factory(
        private val service: SampleService
    ) : ViewModelProvider.NewInstanceFactory() {

        @Suppress("unchecked_cast")
        override fun <T : ViewModel> create(modelClass: Class<T>) =
            SimpleViewModel(service) as T
    }
}

ViewModel ではコンストラクタ経由でさきほどの SampleService を受け取りたいため、コンストラクタ引数に SampleService を追加します。

class SimpleViewModel(
    private val service: SampleService
) : ViewModel() {
    ︙
}

次に、ViewModel の Factory を作成します。

ViewModel の Factory では、ViewModelProvider.NewInstanceFactory を継承して、コンストラクタ引数に SampleService を追加します。この ViewModelProvider.NewInstanceFactory は ViewModelProvider.Factory を継承元に持ち、引数なしのコンストラクタで ViewModel を生成する Factory です。

そして、create メソッドをオーバーライドします。このメソッドは ViewModel を生成するメソッドなので、SimpleViewModel を生成して、それを返します。

    class Factory(
        private val service: SampleService
    ) : ViewModelProvider.NewInstanceFactory() {

        @Suppress("unchecked_cast")
        override fun <T : ViewModel> create(modelClass: Class<T>) =
            SimpleViewModel(service) as T
    }

最後に、ViewModel の利用側を作成します。

今回は、Activity で viewModels を使って ViewModel を取得します。このとき、viewModels に ViewModel の Factory のインスタンスを返すラムダを渡します。こうすることで、ViewModelProvider は渡された Factory を使って、ViewModel を生成します。

class MainActivity : AppCompatActivity(R.layout.main_activity) {

    private val viewModel: SimpleViewModel by viewModels {
        SimpleViewModel.Factory(SampleServiceImpl())
    }
    ︙
}

以上で、コンストラクタ経由で ViewModel に値を渡せるようになります。

関連記事

ViewModel の使用方法については、以下で解説しています。 tatsurotech.hatenablog.com