Tatsuro のテックブログ

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

【Android × Kotlin】LiveData で値を監視する

今回は、LiveData で値を監視する方法を解説します。

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

  • Android Studio Bumblebee | 2021.1.1 Patch 3
  • JDK 11.0.11
  • Android Gradle Plugin 7.1.3
  • Kotlin 1.6.21
  • Gradle 7.4.2
  • androidx.lifecycle 2.4.1

LiveData とは?

LiveData はデータホルダークラスであり、内部に値を保持します。そして、LiveData は保持する値の変更を監視し、その変更を外部にコールバックで通知します。 よって、LiveData を使用することで、値の変更に追従できるようになります。

また、LiveData は Activity や Fragment などのライフサイクルを考慮して値の変更を通知する機能も持ちます。具体的には、Activity や Fragment がフォアグラウンドに表示されているときだけ通知を行い、バックグラウンドにいるときは通知を行わない、といったことが可能です。これにより、メモリリークやクラッシュを回避できます。

LiveData を使う

実際に LiveData を試します。

ViewModel の解説のときと同様に、Button をクリックした回数を ViewModel に保持し、その回数を Activity の TextView に表示してみます。

このとき、Button のクリック回数を素の Int 型で保持するのではなくて、LiveData で保持します。

ライブラリのインポート

LiveData を使えるようにするために、アプリの build.gradle ファイルに次の依存関係を追加します。

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

lifecycle-livedata-ktx をインポートすることにより、LiveData が使えるようになります。また、以降のサンプルでは LiveData を ViewModel に保持するため、lifecycle-viewmodel-ktx もインポートします。

ViewModel で値を保持する

まず、ViewModel を以下のように実装します。

class MainViewModel : ViewModel() {

    /** Button のクリック回数 */
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> get() = _count

    /** クリック回数をカウントアップする。 */
    fun countUp() {
        val value = count.value!!
        _count.value = value + 1
    }
}

最初に、LiveData で値を保持する箇所を見ていきます。

    /** Button のクリック回数 */
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> get() = _count

LiveData には大きく分けて MutableLiveData と LiveData の 2 つのクラスが存在します。この 2 つは以下のような違いがあります。

クラス 特徴
MutableLiveData 値を変更できる。
LiveData 値を変更できない。

LiveData を扱う場合、値の変更は ViewModel 内部に留めて、値の変更通知を受け取る Activity や Fragment などではその値を変更できないようにします。

よって、値そのものは private が付与された MutableLiveData で保持して、それを LiveData に変換したものを ViewModel の外部に公開します。

次に、クリック回数をカウントアップするメソッドを見ていきます。

    /** クリック回数をカウントアップする。 */
    fun countUp() {
        val value = count.value!!
        _count.value = value + 1
    }

LiveData で保持する値をカウントアップするためには、LiveData からの値の取得と LiveData への値の設定が必要です。これらは以下の 2 つで実現します。

  • 値の取得 → LiveData#getValue
  • 値の設定 → MutableLiveData#setValue

また、LiveData を扱うときの注意点として、LiveData は値を null 許容型で保持する、というものがあります。よって、 LiveData から取得した値を扱う場合、その値を null 安全な型にアンラップする必要があります。

以上により、クリック回数のカウントアップメソッドでは、以下のような流れでクリック回数をカウントアップします。

  1. count からクリック回数を取得して、null 安全な型にアンラップする。
  2. クリック回数を +1 加算して、_count に設定する。

Activity で値の変更通知を受け取る

そして、Activity を以下のように実装します。

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

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val viewModel =
            ViewModelProvider(this)[MainViewModel::class.java]

        // ボタンがクリックされるたびに、クリック回数をカウントアップする。
        findViewById<Button>(R.id.button)
            .setOnClickListener {
                viewModel.countUp()
            }

        // クリック回数が変更されたら、TextView を更新する。
        viewModel.count.observe(this) {
            findViewById<TextView>(R.id.text).text = it.toString()
        }
    }
}

まず、Button がクリックされたら ViewModel のカウントアップメソッドを呼びます。

そして、ViewModel のクリック回数の変更通知を監視します。変更通知を監視するために、以下の LiveData#observe メソッドを呼びます。

@MainThread
public void observe(@NonNull LifecycleOwner owner,
                    @NonNull Observer<? super T> observer)

このメソッドに、監視を行う owner と変更通知を受け取ったときに呼び出す observer コールバックを渡します。

owner には、LifecycleOwner インターフェースを継承しているインスタンスを渡します。今回の場合、監視を行うのは Activity であり、MainActivity は LifecycleOwner インターフェースを継承しているので、owner には this を渡します。この owner を渡すことにより、LiveData は owner がアクティブなとき、つまり MainActivity がフォアグラウンドに表示されているときだけ、変更を通知するようになります。

observer コールバックは、変更後のクリック回数を受け取ります。コールバックでは、受け取ったクリック回数を TextView に設定します。

参考

LiveData の概要

関連記事

他の LiveData の変更を監視する方法については、以下で解説しています。

tatsurotech.hatenablog.com

LiveData を変換する方法については、以下で解説しています。

tatsurotech.hatenablog.com

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

tatsurotech.hatenablog.com