All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.arch.lifecycle.LiveData Maven / Gradle / Ivy

/*
 * 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 android.arch.lifecycle;

import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
import static android.arch.lifecycle.Lifecycle.State.STARTED;

import java.util.Iterator;
import java.util.Map;

import android.arch.core.executor.ArchTaskExecutor;
import android.arch.core.internal.SafeIterableMap;
import android.support.annotation.MainThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

/**
 * 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 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 * */ public abstract class LiveData { /** The m data lock. */ private final Object mDataLock = new Object(); /** The Constant START_VERSION. */ static final int START_VERSION = -1; /** The Constant NOT_SET. */ private static final Object NOT_SET = new Object(); /** The m observers. */ private SafeIterableMap, ObserverWrapper> mObservers = new SafeIterableMap<>(); /** The m active count. */ // how many observers are in active state private int mActiveCount = 0; /** The m data. */ private volatile Object mData = NOT_SET; // when setData is called, we set the pending data and actual data swap happens on the main /** The m pending data. */ // thread private volatile Object mPendingData = NOT_SET; /** The m version. */ private int mVersion = START_VERSION; /** The m dispatching value. */ private boolean mDispatchingValue; /** The m dispatch invalidated. */ @SuppressWarnings("FieldCanBeLocal") private boolean mDispatchInvalidated; /** The m post value runnable. */ private final Runnable mPostValueRunnable = new Runnable() { @Override public void run() { Object newValue; synchronized (mDataLock) { newValue = mPendingData; mPendingData = NOT_SET; } //noinspection unchecked setValue((T) newValue); } }; /** * Consider notify. * * @param observer the observer */ 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); } /** * Dispatching value. * * @param initiator the initiator */ 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) { 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) { 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; } /** * Gets the version. * * @return the version */ 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; } /** * An asynchronous update interface for receiving notifications * about LifecycleBound information as the LifecycleBound is constructed. */ class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver { /** The m owner. */ @NonNull final LifecycleOwner mOwner; /** * This method is called when information about an LifecycleBound * which was previously requested using an asynchronous * interface becomes available. * * @param owner the owner * @param observer the observer */ LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer observer) { super(observer); mOwner = owner; } /* (non-Javadoc) * @see android.arch.lifecycle.LiveData.ObserverWrapper#shouldBeActive() */ @Override boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } /* (non-Javadoc) * @see android.arch.lifecycle.GenericLifecycleObserver#onStateChanged(android.arch.lifecycle.LifecycleOwner, android.arch.lifecycle.Lifecycle.Event) */ @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { removeObserver(mObserver); return; } activeStateChanged(shouldBeActive()); } /* (non-Javadoc) * @see android.arch.lifecycle.LiveData.ObserverWrapper#isAttachedTo(android.arch.lifecycle.LifecycleOwner) */ @Override boolean isAttachedTo(LifecycleOwner owner) { return mOwner == owner; } /* (non-Javadoc) * @see android.arch.lifecycle.LiveData.ObserverWrapper#detachObserver() */ @Override void detachObserver() { mOwner.getLifecycle().removeObserver(this); } } /** * The Class ObserverWrapper. */ private abstract class ObserverWrapper { /** The m observer. */ final Observer mObserver; /** The m active. */ boolean mActive; /** The m last version. */ int mLastVersion = START_VERSION; /** * Instantiates a new observer wrapper. * * @param observer the observer */ ObserverWrapper(Observer observer) { mObserver = observer; } /** * Should be active. * * @return true, if successful */ abstract boolean shouldBeActive(); /** * Checks if is attached to. * * @param owner the owner * @return true, if is attached to */ boolean isAttachedTo(LifecycleOwner owner) { return false; } /** * Detach observer. */ void detachObserver() { } /** * Active state changed. * * @param newActive the new active */ 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); } } } /** * An asynchronous update interface for receiving notifications * about AlwaysActive information as the AlwaysActive is constructed. */ private class AlwaysActiveObserver extends ObserverWrapper { /** * This method is called when information about an AlwaysActive * which was previously requested using an asynchronous * interface becomes available. * * @param observer the observer */ AlwaysActiveObserver(Observer observer) { super(observer); } /* (non-Javadoc) * @see android.arch.lifecycle.LiveData.ObserverWrapper#shouldBeActive() */ @Override boolean shouldBeActive() { return true; } } /** * Assert main thread. * * @param methodName the method name */ private static void assertMainThread(String methodName) { if (!ArchTaskExecutor.getInstance().isMainThread()) { throw new IllegalStateException("Cannot invoke " + methodName + " on a background" + " thread"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy