LiveDataの基礎的な性質を整理する。


はじめに

Architecture Componentsで導入されたLiveDataは、ライフサイクルに連動したアクティブ状態を持つ事が特徴的です。LiveDataの基本的な話は、開発者サイトの記事12345を見ておけば大体の事が把握できるかと思いますが、アクティブとなる条件やObserverへの通知タイミングに関する説明が分散していて分かり難い印象を持ちました。

という事で、LiveDataの基礎的な性質を、自分なりに整理してみる事にしました。

LiveData編

LiveData自身とobserve()/observeForever()で登録したObserverについて、アクティブとなる条件やObserverへの通知タイミングを整理します。

Observerがアクティブとなる条件

observe()で登録したObserverは、以下の条件でアクティブとなる。

  • LifecycleがSTARTED又はRESUMEDとなった時。

observeForever()で登録したObserverは、以下の条件でアクティブとなる。

  • 常にアクティブ。

LiveDataがアクティブとなる条件

LiveDataは、以下を満たす時にアクティブとなる。

  • 登録中のObserverが1つ以上アクティブである時。

setValue()した値がObserverに通知されるタイミング

Observerの状態 onChanged()のタイミング
(a) アクティブ 即座に通知
(b) 非アクティブ 次回アクティブ時に通知
(c) 未登録 登録後の次回アクティブ時に通知
  • LiveDataの初期値はnull。一度もsetValueしていないならば、onChanged()は通知されない。
  • setValue()毎の通知は、登録中のObserver別に一度だけ通知される。6
  • 複数のObserverを登録している時は、それぞれのObserverのアクティブ状態に応じて通知される。

MediatorLiveData編

MediatorLiveDataのaddSource()で設定したLiveDataとObserverについて、アクティブとなる条件やObserverへの通知タイミングを整理します。7

addSource()でObserverを設定するコード例

本章ではLiveDataの役者を明確にするため、以下のコード例を元に説明します。

    LiveData<SourceValueType> sourceData = ();
    MediatorLiveData<XXX> mediatorData = ();
    mediatorData.addSource(sourceData, new Observer<SourceValueType>() {
        @Override
        public void onChanged(@Nullable SourceValueType data) {
            // addSource()で設定したObserverは、sourceDataのObserverとして扱われる。
            // sourceDataからの通知に応じて、ここに任意の処理を実施する。
        }
    });

Observerがアクティブとなる条件

addSource()で設定したObserverは、以下の条件でアクティブとなる。

  • mediatorDataがアクティブとなった時。

補足: addSource()で設定したObserverは、以下の契機でsourceDataに対して登録/解除される。8

  • mediatorDataがアクティブへ変化した時は、sourceDataにアクティブなObserverとして登録される。
  • mediatorDataが非アクティブへ変化した時は、sourceDataのObserverから解除される。 9

sourceDataがアクティブとなる条件

sourceDataは、以下のどちらかを満たす時にアクティブとなる。

  • mediatorDataがアクティブとなった時。
  • 上述の「LiveDataがアクティブとなる条件」を満たす時。

sourceDataにsetValue()した値がObserverに通知されるタイミング

Observerの状態 onChanged()のタイミング
(a) アクティブ 即座に通知
(b) 非アクティブ 次回アクティブ時に通知
(c) addSource()で未設定 設定後の次回アクティブ時に通知
  • sourceDataからのsetValue()毎の通知は、addSource()で設定中のObserver別に一度だけ通知される。10

付録: Unit Testで試してみる。

LiveDataの基礎的な動作はLocal unit testでも検証する事ができます。observe()のためのLifecycleOwnerは、以下のようなコードで作成することができます。

StubLifecycleOwner
public class StubLifecycleOwner implements LifecycleOwner {
    private final LifecycleRegistry registry = new LifecycleRegistry(this);

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return registry;
    }
    public void markInitialized() {
        registry.markState(Lifecycle.State.INITIALIZED);
    }
    public void markCreated() {
        registry.markState(Lifecycle.State.CREATED);
    }
    public void markStarted() {
        registry.markState(Lifecycle.State.STARTED);
    }
    public void markResumed() {
        registry.markState(Lifecycle.State.RESUMED);
    }
    public void markDestroyed() {
        registry.markState(Lifecycle.State.DESTROYED);
    }
}

テストコード例は以下な感じ。

LiveDataTest
@RunWith(JUnit4.class)
public class LiveDataTest {

    @Rule
    public InstantTaskExecutorRule instantExecutorRule = new InstantTaskExecutorRule();

    @Test
    public void testSubscribe() {
        StubLifecycleOwner owner = new StubLifecycleOwner();
        Observer<Long> observer = mock(Observer.class);
        MutableLiveData<Long> liveData = new MutableLiveData<>();
        // Observerが非アクティブ
        owner.markCreated();
        liveData.observe(owner, observer);
        // 非アクティブ時のsetValue
        liveData.setValue(1L);
        // 即座に通知されない
        verify(observer, never()).onChanged(anyLong());
        // Observerがアクティブ
        owner.markStarted();
        // (b) 次回アクティブ時に通知
        verify(observer).onChanged(1L);
    }
}

  1. Architecture Components - Solving the Lifecycle Problem (Google I/O '17) 

  2. Architecture Components > Guide to App Architecture 

  3. Architecture Components > LiveData 

  4. Reference > LiveData 

  5. Reference > MediatorLiveData 

  6. 一度通知されたならば、次回アクティブ時になっても通知は行われない。ただしObserverが解除されたならば(c)に従う。もちろんだが、次にsetValue()されたならばObserverの状態に従って通知が行われる。 

  7. Transformationsの変換メソッドは、内部実装としてMediatorLiveDataを使用している。map()やswitchMap()の引数に設定したLiveDataは、addSource()による設定時と同様の影響を受ける。 

  8. mediatorDataのアクティブ状態がLifecycleの状態に連動している状況下では、例えばアクティビティを閉じた際にmediatorDataが非アクティブとなるため、addSource()で指定したObserverへの通知が堰き止めされる。 

  9. これによりsourceDataからmediatorDataへの参照関係が無くなるため、明示的にremoveSource()せずとも、mediatorDataがGCの回収対象となるようだ。 

  10. つまり。ポリシーはLiveDataと一緒。