LiveData の observer 側の onChanged() がトリガーされるタイミング

source に対する setValue() がトリガーとなる

漠然とサンプルを参考にして LiveData を使い始めてきたので、複数の LiveData の処理がされるタイミングの前後関係が意図した通りにならずに困ってしまった。

MediatorLiveData や Transformations.map / Transformations.switchMap では lambda 表現で onChanged() を定義するが、この onChanged() の処理が、監視先の source の変更よりも先になってしまう現象が発生し、頭を抱えてしまった。

実際のところ、MediatorLiveData や Transformations.* で潜在的に行っていることにおいて、MutableLiveData が使われており、本来的に、source の変更が observer 側に伝えられるタイミングは、setValue() / postValue() であると明記されている。

いずれの場合でも、setValue() または postValue() を呼び出すことによってオブザーバーがトリガーされ、UI が更新されます。

公式ガイド「LiveData オブジェクトを更新する

つまり、Transformations.* では source 側 onChanged() の末尾に隠蔽されているので曖昧に理解してしまっていたが、MediatorLiveData の onChanged() の場合はその中で source 自身が setValue() / postValue() された直後に、observer 側の onChanged() が実行されることになる。そのため、source 側が MediatorLiveData の場合に、必ずしも source 側の変更が先にならないといった現象が発生していたのである。「source が先、observer が後」ではなく、「source の setValue() / postValue() が先、observer の onChanged() が後」というのが正確だったわけである。

active でないと observe 自体が有効にならない

もう一つハマったのが、source が更新されているにもかかわらず、そもそも onChanged() が実行されないという現象。実は、LiveData には active / inactive という状態フラグがあり、inactive の場合にはいくら onChanged() を適切に定義しても機能しないという罠がある。LiveData が View 側で利用されている場合には当然、active になるので、意識する必要はない。一方、ViewModel 中で他の LiveData へと中継するために定義されているような中間オブジェクトの場合、他の LiveData オブジェクトから observe されていない状態に陥っていると inactive なため、その onChanged() で期待される挙動が発動しない。他の active な LiveData が中継用 LiveData を source としてobserve する定義を記述せず、中継 LiveData 側の onChanged() 内で他の LiveData を(ついでに)setValue() するような記述にしてしまっている場合に要注意である。

参考:Android MediatorLiveData observer

コメント

このブログの人気の投稿

清水俊史『上座部仏教における聖典論の研究』

シークエンスパパともの先見の明

シークエンスパパとも 本物の霊能力