MutableLiveDataのデータ更新
前節のオブザーバ登録で、LiveData内に保持したデータの変更に応じて、自動で画面表示が切り替わるようになりました。次に、そのデータの変更方法を紹介していきます。
データ登録はsetValue()
リスト1やリスト2で、MainViewModel内にLiveDataオブジェクトとして_cocktailmemoLiveDataが用意できました。ただし、この_cocktailmemoLiveData内のCocktailmemoオブジェクトはnullのままです。この状態で、_cocktailmemoLiveData内のCocktailmemoオブジェクトを利用しようとすると、NullPointerExceptionが発生してしまいます。そこで、MainViewModelの生成と同時に、_cocktailmemoLiveData内に、初期値にあたるCocktailmemoオブジェクトを格納することにします。これは、Javaコードでは、リスト7のようなコードになります。
public class MainViewModel extends AndroidViewModel { : public MainViewModel(Application application) { : Cocktailmemo cocktailmemo = new Cocktailmemo(); // (1) cocktailmemo.id = -1; // (2) cocktailmemo.name = "未選択"; // (3) cocktailmemo.note = ""; // (4) _cocktailmemoLiveData.setValue(cocktailmemo); // (5) } : }
注目すべきはリスト7の(5)です。MutableLiveDataにデータを格納するメソッドは、setValue()です。このメソッドを実行することで、引数のデータが内部に格納されると同時に、前節で作成したオブザーバが呼び出されます。その際、このsetValue()メソッドで渡されたデータが、オブザーバのonChanged()メソッドの引数として渡されます。
リスト7では、このsetValue()を実行する前に、あらかじめデータを用意しています。それが(1)〜(4)であり、これを初期値としています。この初期値は、リストからカクテルが全く選択されていない状態とするので、(2)のようにidは-1、(3)のようにカクテル名のnameは「未選択」、感想のnoteは空文字としています。
Kotlinコードも同様で、リスト8のようになります。
class MainViewModel(application: Application) : AndroidViewModel(application) { : init { : val cocktailmemo = Cocktailmemo(-1, "未選択", "") cocktailmemoLiveData.value = cocktailmemo } : }
非同期でのデータ登録とデータ取得
前項で紹介したデータ登録であるsetValue()メソッド、および、valueプロパティへの代入は、その処理がUIスレッドで行われる場合のコードです。LiveDataは、非同期でのデータ登録にも対応しています。もしデータ登録処理を、ワーカースレッドで行う場合は、JavaとKotlinともに、postValue()メソッドを利用します。LiveDataはワーカースレッドで行われたデータ登録を検知し、UIスレッド上でオブザーバを自動実行します。
また、LiveDataに格納されたデータを取得したい場合は、JavaではgetValue()メソッドを、Kotlinではvalueプロパティを利用します。ただし、これまでも述べたように、LiveData内部へのデータ格納は、非同期で行われることが多々あります。そのため、場合によっては、getValue()メソッドやvalueプロパティへアクセスしたタイミングでは、まだ適切にデータが格納されていないことが多々あります。この点には注意しておいてください。
DBから取得したデータの格納(Java版)
これで、一通り、LiveDataへのデータ格納方法を紹介したことになります。この方法を利用して、前回のリスト10やリスト11で用意したようなprepareCocktailNote()メソッド、すなわち、カクテルリストが選択された際にデータベースから取得したデータを扱うメソッドがどのように変わるかを紹介します。ただし、LiveDataを利用すると、感想欄データだけなく、全てのデータをひとまとめに扱うため、メソッド名をprepareCocktailmemo()とし、Javaコードではリスト9のようになります。
public class MainViewModel extends AndroidViewModel { : public void prepareCocktailmemo(int cocktailId, String cocktailName) { Cocktailmemo cocktailmemo = _cocktailmemoRepository.getCocktailmemo(cocktailId); // (1) if(cocktailmemo == null) { // (2) cocktailmemo = new Cocktailmemo(); cocktailmemo.id = cocktailId; cocktailmemo.name = cocktailName; cocktailmemo.note = ""; } _cocktailmemoLiveData.setValue(cocktailmemo); // (3) } : }
リスト9には、特に難しいことはありません。(1)でリポジトリから指定された主キーに該当するCocktailmemoオブジェクトを取得しています。その際、前回のコードではフィールドに保持していた_cocktailIdを、指定主キーとして利用していました。しかし、今回はそのようなフィールドは存在しないので、引数でもらうことにしています。ただし、前回同様、このcocktailmemoはnullの可能性があります。そこで、(2)のifブロック内では、新たなCocktailmemoオブジェクトを生成し、今選択したカクテルのidと名前を引数でもらい、それを格納するようにしています。そのようにして用意したcocktailmemoをLiveDataに格納しているのが、(3)です。
このようなprepareCocktailmemo()メソッドを用意しておくと、カクテルリストが選択された際のMainActivity側のコードは、リスト10のように、非常にシンプルになります。UI更新処理が全く含まれていないのが特徴です。
String cocktailName = (String) parent.getItemAtPosition(position); _mainViewModel.prepareCocktailmemo(position, cocktailName);
DBから取得したデータの格納(Kotlin版)
Kotlinコードも同様で、prepareCocktailmemo()メソッドは、リスト11のようになります。
class MainViewModel(application: Application) : AndroidViewModel(application) { : fun prepareCocktailmemo(cocktailId: Int, cocktailName: String) { viewModelScope.launch { var cocktailmemo = _cocktailmemoRepository.getCocktailmemo(cocktailId) if(cocktailmemo == null) { cocktailmemo = Cocktailmemo(cocktailId, cocktailName, "") } cocktailmemoLiveData.value = cocktailmemo } } : }
ほぼ、リスト10のJavaコードをKotlinコードに置き換えた内容になっています。ただし、リスト11で特徴的なのは、戻り値がなくなっていることです。前回のリスト11ではコルーチンへの対応のため、Jobオブジェクトを戻り値としていましたが、LiveDataを利用すると、その必要がなくなります。MainActivity側のコードは、リスト12のようになり、前回のリスト5で用意していたコルーチンスコープが不要となります。
val cocktailName = parent.getItemAtPosition(position) as String _mainViewModel.prepareCocktailmemo(position, cocktailName);
まとめ
Android Jetpackについて紹介していく本連載の第4回は、いかがでしたでしょうか。
今回は、データ更新に応じてUI変更を自動実行できるLiveDataを紹介しました。このLiveDataを利用することで、アクティビティやフラグメント内のUI変更処理をオブザーバにまとめることができるようになります。
実は、このLiveDataを自動生成できる仕組みがRoomにはあります。その仕組みを利用した方が便利な場合もあります。次回は、そのRoomによるLiveDataの自動生成とLiveDataに似た仕組みであるKotlinのFlowを紹介します。