Mokelab Blog

Android Jetpack の LiveData と ViewModel

今回から Android Jetpack に関する記事を何回かにわけて公開します。まずは Architecture Components として提供されていた LiveData と ViewModel について解説します。

LiveData とは

LiveData とはデータの入れ物で、中身を監視(observe)することができます。監視する際、アクティビティやフラグメントといった LifecycleOwner を一緒に渡すことで、UI に反映すべき時のみ中身の変更通知を行ってくれたり、不要になった時に監視をキャンセルしてくれたりします。 その他のメリットは公式ドキュメント を参照してください。

ViewModel とは

ViewModel はアクティビティやフラグメントに対するデータ置き場の役割を担います。画面回転やウィンドウのサイズが変更されると、アクティビティやフラグメントインスタンスは再生成されますが、この ViewModel はインスタンスが保持されます。通常は先ほど紹介した LiveData インスタンスを ViewModel で保持します。

プロジェクトに LiveData と ViewModel を追加する

LiveData と ViewModel は Jetpack のライブラリとして提供されています。どちらも androidx.appcompat の依存ライブラリとなっているので、追加の作業は不要です。

(本来は Kotlin 用拡張(ktx)の追加が必要なのですが、どうも Navigation の ktx 経由で追加されているようです。本記事は Navigation も追加されているものとして説明を続けます。)

今回のサンプルアプリ

LiveData と ViewModel の使い方を体験するためのサンプルとして、テキストを入力し Submit ボタンを押すとテキストが表示されるアプリを作ってみます。

ViewModel クラスを作る

まずは LiveData 置き場となる ViewModel クラスを作ります。フラグメント毎に作ることになるので、今回は MainViewModel としました。

class MainViewModel: ViewModel() {
    // まだ、からっぽ
}

MutableLiveData を持たせる

今回の画面にはデータとして名前があるので、それを保持するための LiveData を ViewModel に持たせます。データをセットし、監視している人にデータが届く方式でよいので、MutableLiveData とします。

class MainViewModel: ViewModel() {
    val name = MutableLiveData<String>()
}

MainViewModel を取ってくる

次に、フラグメント側で MainViewModel を取ってくるコードを書きます。Kotlin の場合は次のようにby viewModels() をつけるだけであとはいい感じにしてくれます。

class MainFragment : Fragment() {

    private val mainVM: MainViewModel by viewModels()

    // 中略
}

なお、Architecture Components が登場したときは ViewModelProviders.of(this).get(MainViewModel::class.java)で ViewModel オブジェクトを取得していましたが、いつの間にか ViewModelProviders クラスはなくなっています。上記のように by viewModels()で取得するようにしましょう(Java の場合はどうするのが正解なのでしょうか。。。)。

余談:ViewBinding を使う

Android Studio 3.6 ベータあたりから ViewBinding が使えるようになっています。build.gradle の android の中に次の記述を加えるだけで使えるようになります。

android {
    ...

    viewBinding {
        enabled = true
    }
}

有効にすると、データバインディングの時と同様にレイアウト XML 名を使ったクラスが自動生成されます。今回は main_fragment.xml としたので、MainFragmentBinding というクラスを使います。

class MainFragment : Fragment() {

    private val mainVM: MainViewModel by viewModels()

    private lateinit var binding: MainFragmentBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        this.binding = MainFragmentBinding.inflate(inflater, container, false)

        return this.binding.root
    }
}

LiveData を監視する

ここまできたら後は監視し、変更があったら View に反映させるだけです。onViewCreated()で次のように observe を呼びます。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    mainVM.name.observe(this, Observer {
        // itに最新のnameの値が入っている
        binding.resultText.text = getString(R.string.result_message, it)
    })
}

observe()の第1引数には LifecycleOwner としてフラグメントを渡しています。これで、値の変更はフラグメントがアクティブな時のみ行われ、 バックボタンなどでフラグメントが破棄されたときは監視の終了処理が自動で行われます。

MutableLiveData に値をいれる

最後に、ボタンをタップしたら入力内容を MutableLiveData にいれる部分です。

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    this.binding = MainFragmentBinding.inflate(inflater, container, false)
    // ボタンがタップされたら、値をMutableLiveDataにいれる
    this.binding.submitButton.setOnClickListener {
        mainVM.name.value = binding.nameEdit.text.toString()
        binding.nameEdit.setText("")
    }
    return this.binding.root
}

なお、ワーカースレッドでの処理結果を MutableLiveData に入れたい場合は次のように postValue()を使用します。

mainVM.name.postValue(binding.nameEdit.text.toString())

まとめ

LiveData と ViewModel の使い方について簡単に説明しました。定番のパターンとして使えるようになっておきましょう。

本サイトではサービス向上のため、Google Analyticsを導入しています。分析にはCookieを利用しています。