/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package androidx.lifecycle; import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.STARTED; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.arch.core.internal.SafeIterableMap; import androidx.arch.core.executor.ArchTaskExecutor; import java.util.Iterator; import java.util.Map; /** * LiveData is a data holder class that can be observed within a given lifecycle. * This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and * this observer will be notified about modifications of the wrapped data only if the paired * LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is * {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via * {@link #observeForever(Observer)} is considered as always active and thus will be always notified * about modifications. For those observers, you should manually call * {@link #removeObserver(Observer)}. * *

An observer added with a Lifecycle will be automatically removed if the corresponding * Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for * activities and fragments where they can safely observe LiveData and not worry about leaks: * they will be instantly unsubscribed when they are destroyed. * *

* In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods * to get notified when number of active {@link Observer}s change between 0 and 1. * This allows LiveData to release any heavy resources when it does not have any Observers that * are actively observing. *

* This class is designed to hold individual data fields of {@link ViewModel}, * but can also be used for sharing data between different modules in your application * in a decoupled fashion. * * @param The type of data held by this instance * @see ViewModel */ public abstract class LiveData { private final Object mDataLock = new Object(); static final int START_VERSION = -1; private static final Object NOT_SET = new Object(); private SafeIterableMap, ObserverWrapper> mObservers = new SafeIterableMap<>(); // how many observers are in active state private int mActiveCount = 0; private volatile Object mData = NOT_SET; // when setData is called, we set the pending data and actual data swap happens on the main // thread private volatile Object mPendingData = NOT_SET; private int mVersion = START_VERSION; private boolean mDispatchingValue; @SuppressWarnings("FieldCanBeLocal") private boolean mDispatchInvalidated; private final Runnable mPostValueRunnable = new Runnable() { @Override public void run() { Object newValue; synchronized (mDataLock) { newValue = mPendingData; mPendingData = NOT_SET; } //noinspection unchecked setValue((T) newValue); } }; private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) { return; } // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet. // // we still first check observer.active to keep it as the entrance for events. So even if // the observer moved to an active state, if we've not received that event, we better not // notify for a more predictable notification order. if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; //noinspection unchecked observer.mObserver.onChanged((T) mData); } private void dispatchingValue(@Nullable ObserverWrapper initiator) { if (mDispatchingValue) { mDispatchInvalidated = true; return; } mDispatchingValue = true; do { mDispatchInvalidated = false; if (initiator != null) { considerNotify(initiator); initiator = null; } else { for (Iterator, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; } /** * Adds the given observer to the observers list within the lifespan of the given * owner. The events are dispatched on the main thread. If LiveData already has data * set, it will be delivered to the observer. *

* The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED} * or {@link Lifecycle.State#RESUMED} state (active). *

* If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will * automatically be removed. *

* When data changes while the {@code owner} is not active, it will not receive any updates. * If it becomes active again, it will receive the last available data automatically. *

* LiveData keeps a strong reference to the observer and the owner as long as the * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to * the observer & the owner. *

* If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData * ignores the call. *

* If the given owner, observer tuple is already in the list, the call is ignored. * If the observer is already in the list with another owner, LiveData throws an * {@link IllegalArgumentException}. * * @param owner The LifecycleOwner which controls the observer * @param observer The observer that will receive the events */ @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) { assertMainThread("observe"); if (owner.getLifecycle().getCurrentState() == DESTROYED) { // ignore return; } LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } if (existing != null) { return; } owner.getLifecycle().addObserver(wrapper); } /** * Adds the given observer to the observers list. This call is similar to * {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which * is always active. This means that the given observer will receive all events and will never * be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop * observing this LiveData. * While LiveData has one of such observers, it will be considered * as active. *

* If the observer was already added with an owner to this LiveData, LiveData throws an * {@link IllegalArgumentException}. * * @param observer The observer that will receive the events */ @MainThread public void observeForever(@NonNull Observer observer) { assertMainThread("observeForever"); AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer); ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } if (existing != null) { return; } wrapper.activeStateChanged(true); } /** * Removes the given observer from the observers list. * * @param observer The Observer to receive events. */ @MainThread public void removeObserver(@NonNull final Observer observer) { assertMainThread("removeObserver"); ObserverWrapper removed = mObservers.remove(observer); if (removed == null) { return; } removed.detachObserver(); removed.activeStateChanged(false); } /** * Removes all observers that are tied to the given {@link LifecycleOwner}. * * @param owner The {@code LifecycleOwner} scope for the observers to be removed. */ @SuppressWarnings("WeakerAccess") @MainThread public void removeObservers(@NonNull final LifecycleOwner owner) { assertMainThread("removeObservers"); for (Map.Entry, ObserverWrapper> entry : mObservers) { if (entry.getValue().isAttachedTo(owner)) { removeObserver(entry.getKey()); } } } /** * Posts a task to a main thread to set the given value. So if you have a following code * executed in the main thread: *

     * liveData.postValue("a");
     * liveData.setValue("b");
     * 
* The value "b" would be set at first and later the main thread would override it with * the value "a". *

* If you called this method multiple times before a main thread executed a posted task, only * the last value would be dispatched. * * @param value The new value */ protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { postTask = mPendingData == NOT_SET; mPendingData = value; } if (!postTask) { return; } ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); } /** * Sets the value. If there are active observers, the value will be dispatched to them. *

* This method must be called from the main thread. If you need set a value from a background * thread, you can use {@link #postValue(Object)} * * @param value The new value */ @MainThread protected void setValue(T value) { assertMainThread("setValue"); mVersion++; mData = value; dispatchingValue(null); } /** * Returns the current value. * Note that calling this method on a background thread does not guarantee that the latest * value set will be received. * * @return the current value */ @Nullable public T getValue() { Object data = mData; if (data != NOT_SET) { //noinspection unchecked return (T) data; } return null; } int getVersion() { return mVersion; } /** * Called when the number of active observers change to 1 from 0. *

* This callback can be used to know that this LiveData is being used thus should be kept * up to date. */ protected void onActive() { } /** * Called when the number of active observers change from 1 to 0. *

* This does not mean that there are no observers left, there may still be observers but their * lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED} * (like an Activity in the back stack). *

* You can check if there are observers via {@link #hasObservers()}. */ protected void onInactive() { } /** * Returns true if this LiveData has observers. * * @return true if this LiveData has observers */ @SuppressWarnings("WeakerAccess") public boolean hasObservers() { return mObservers.size() > 0; } /** * Returns true if this LiveData has active observers. * * @return true if this LiveData has active observers */ @SuppressWarnings("WeakerAccess") public boolean hasActiveObservers() { return mActiveCount > 0; } class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver { @NonNull final LifecycleOwner mOwner; LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer observer) { super(observer); mOwner = owner; } @Override boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { removeObserver(mObserver); return; } activeStateChanged(shouldBeActive()); } @Override boolean isAttachedTo(LifecycleOwner owner) { return mOwner == owner; } @Override void detachObserver() { mOwner.getLifecycle().removeObserver(this); } } private abstract class ObserverWrapper { final Observer mObserver; boolean mActive; int mLastVersion = START_VERSION; ObserverWrapper(Observer observer) { mObserver = observer; } abstract boolean shouldBeActive(); boolean isAttachedTo(LifecycleOwner owner) { return false; } void detachObserver() { } void activeStateChanged(boolean newActive) { if (newActive == mActive) { return; } // immediately set active state, so we'd never dispatch anything to inactive // owner mActive = newActive; boolean wasInactive = LiveData.this.mActiveCount == 0; LiveData.this.mActiveCount += mActive ? 1 : -1; if (wasInactive && mActive) { onActive(); } if (LiveData.this.mActiveCount == 0 && !mActive) { onInactive(); } if (mActive) { dispatchingValue(this); } } } private class AlwaysActiveObserver extends ObserverWrapper { AlwaysActiveObserver(Observer observer) { super(observer); } @Override boolean shouldBeActive() { return true; } } private static void assertMainThread(String methodName) { if (!ArchTaskExecutor.getInstance().isMainThread()) { throw new IllegalStateException("Cannot invoke " + methodName + " on a background" + " thread"); } } }