LiveData.java revision d8ea6fa753372c38ad46211d871432363d8bd6c8
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.arch.lifecycle;
18
19import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
20import static android.arch.lifecycle.Lifecycle.State.STARTED;
21
22import android.arch.core.executor.AppToolkitTaskExecutor;
23import android.arch.core.internal.SafeIterableMap;
24import android.arch.lifecycle.Lifecycle.State;
25import android.support.annotation.MainThread;
26import android.support.annotation.Nullable;
27
28import java.util.Iterator;
29import java.util.Map;
30
31/**
32 * LiveData is a data holder class that can be observed within a given lifecycle.
33 * This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and
34 * this observer will be notified about modifications of the wrapped data only if the paired
35 * LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is
36 * {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via
37 * {@link #observeForever(Observer)} is considered as always active and thus will be always notified
38 * about modifications. For those observers, you should manually call
39 * {@link #removeObserver(Observer)}.
40 *
41 * <p> An observer added with a Lifecycle will be automatically removed if the corresponding
42 * Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for
43 * activities and fragments where they can safely observe LiveData and not worry about leaks:
44 * they will be instantly unsubscribed when they are destroyed.
45 *
46 * <p>
47 * In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods
48 * to get notified when number of active {@link Observer}s change between 0 and 1.
49 * This allows LiveData to release any heavy resources when it does not have any Observers that
50 * are actively observing.
51 * <p>
52 * This class is designed to hold individual data fields of {@link ViewModel},
53 * but can also be used for sharing data between different modules in your application
54 * in a decoupled fashion.
55 *
56 * @param <T> The type of data hold by this instance
57 * @see ViewModel
58 */
59@SuppressWarnings({"WeakerAccess", "unused"})
60// TODO: Thread checks are too strict right now, we may consider automatically moving them to main
61// thread.
62public abstract class LiveData<T> {
63    private final Object mDataLock = new Object();
64    static final int START_VERSION = -1;
65    private static final Object NOT_SET = new Object();
66
67    private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {
68
69        private LifecycleRegistry mRegistry = init();
70
71        private LifecycleRegistry init() {
72            LifecycleRegistry registry = new LifecycleRegistry(this);
73            registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
74            registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
75            registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
76            return registry;
77        }
78
79        @Override
80        public Lifecycle getLifecycle() {
81            return mRegistry;
82        }
83    };
84
85    private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
86            new SafeIterableMap<>();
87
88    // how many observers are in active state
89    private int mActiveCount = 0;
90    private Object mData = NOT_SET;
91    // when setData is called, we set the pending data and actual data swap happens on the main
92    // thread
93    private volatile Object mPendingData = NOT_SET;
94    private int mVersion = START_VERSION;
95
96    private boolean mDispatchingValue;
97    @SuppressWarnings("FieldCanBeLocal")
98    private boolean mDispatchInvalidated;
99    private final Runnable mPostValueRunnable = new Runnable() {
100        @Override
101        public void run() {
102            Object newValue;
103            synchronized (mDataLock) {
104                newValue = mPendingData;
105                mPendingData = NOT_SET;
106            }
107            //noinspection unchecked
108            setValue((T) newValue);
109        }
110    };
111
112    private void considerNotify(LifecycleBoundObserver observer) {
113        if (!observer.active) {
114            return;
115        }
116        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
117        //
118        // we still first check observer.active to keep it as the entrance for events. So even if
119        // the observer moved to an active state, if we've not received that event, we better not
120        // notify for a more predictable notification order.
121        if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
122            return;
123        }
124        if (observer.lastVersion >= mVersion) {
125            return;
126        }
127        observer.lastVersion = mVersion;
128        //noinspection unchecked
129        observer.observer.onChanged((T) mData);
130    }
131
132    private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
133        if (mDispatchingValue) {
134            mDispatchInvalidated = true;
135            return;
136        }
137        mDispatchingValue = true;
138        do {
139            mDispatchInvalidated = false;
140            if (initiator != null) {
141                considerNotify(initiator);
142                initiator = null;
143            } else {
144                for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
145                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
146                    considerNotify(iterator.next().getValue());
147                    if (mDispatchInvalidated) {
148                        break;
149                    }
150                }
151            }
152        } while (mDispatchInvalidated);
153        mDispatchingValue = false;
154    }
155
156    /**
157     * Adds the given observer to the observers list within the lifespan of the given
158     * owner. The events are dispatched on the main thread. If LiveData already has data
159     * set, it will be delivered to the observer.
160     * <p>
161     * The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
162     * or {@link Lifecycle.State#RESUMED} state (active).
163     * <p>
164     * If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
165     * automatically be removed.
166     * <p>
167     * When data changes while the {@code owner} is not active, it will not receive any updates.
168     * If it becomes active again, it will receive the last available data automatically.
169     * <p>
170     * LiveData keeps a strong reference to the observer and the owner as long as the
171     * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
172     * the observer &amp; the owner.
173     * <p>
174     * If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
175     * ignores the call.
176     * <p>
177     * If the given owner, observer tuple is already in the list, the call is ignored.
178     * If the observer is already in the list with another owner, LiveData throws an
179     * {@link IllegalArgumentException}.
180     *
181     * @param owner    The LifecycleOwner which controls the observer
182     * @param observer The observer that will receive the events
183     */
184    @MainThread
185    public void observe(LifecycleOwner owner, Observer<T> observer) {
186        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
187            // ignore
188            return;
189        }
190        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
191        LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
192        if (existing != null && existing.owner != wrapper.owner) {
193            throw new IllegalArgumentException("Cannot add the same observer"
194                    + " with different lifecycles");
195        }
196        if (existing != null) {
197            return;
198        }
199        owner.getLifecycle().addObserver(wrapper);
200        wrapper.activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
201    }
202
203    /**
204     * Adds the given observer to the observers list. This call is similar to
205     * {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which
206     * is always active. This means that the given observer will receive all events and will never
207     * be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop
208     * observing this LiveData.
209     * While LiveData has one of such observers, it will be considered
210     * as active.
211     * <p>
212     * If the observer was already added with an owner to this LiveData, LiveData throws an
213     * {@link IllegalArgumentException}.
214     *
215     * @param observer The observer that will receive the events
216     */
217    @MainThread
218    public void observeForever(Observer<T> observer) {
219        observe(ALWAYS_ON, observer);
220    }
221
222    /**
223     * Removes the given observer from the observers list.
224     *
225     * @param observer The Observer to receive events.
226     */
227    @MainThread
228    public void removeObserver(final Observer<T> observer) {
229        assertMainThread("removeObserver");
230        LifecycleBoundObserver removed = mObservers.remove(observer);
231        if (removed == null) {
232            return;
233        }
234        removed.owner.getLifecycle().removeObserver(removed);
235        removed.activeStateChanged(false);
236    }
237
238    /**
239     * Removes all observers that are tied to the given {@link LifecycleOwner}.
240     *
241     * @param owner The {@code LifecycleOwner} scope for the observers to be removed.
242     */
243    @MainThread
244    public void removeObservers(final LifecycleOwner owner) {
245        assertMainThread("removeObservers");
246        for (Map.Entry<Observer<T>, LifecycleBoundObserver> entry : mObservers) {
247            if (entry.getValue().owner == owner) {
248                removeObserver(entry.getKey());
249            }
250        }
251    }
252
253    /**
254     * Posts a task to a main thread to set the given value. So if you have a following code
255     * executed in the main thread:
256     * <pre class="prettyprint">
257     * liveData.postValue("a");
258     * liveData.setValue("b");
259     * </pre>
260     * The value "b" would be set at first and later the main thread would override it with
261     * the value "a".
262     * <p>
263     * If you called this method multiple times before a main thread executed a posted task, only
264     * the last value would be dispatched.
265     *
266     * @param value The new value
267     */
268    protected void postValue(T value) {
269        boolean postTask;
270        synchronized (mDataLock) {
271            postTask = mPendingData == NOT_SET;
272            mPendingData = value;
273        }
274        if (!postTask) {
275            return;
276        }
277        AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
278    }
279
280    /**
281     * Sets the value. If there are active observers, the value will be dispatched to them.
282     * <p>
283     * This method must be called from the main thread. If you need set a value from a background
284     * thread, you can use {@link #postValue(Object)}
285     *
286     * @param value The new value
287     */
288    @MainThread
289    protected void setValue(T value) {
290        assertMainThread("setValue");
291        mVersion++;
292        mData = value;
293        dispatchingValue(null);
294    }
295
296    /**
297     * Returns the current value.
298     * Note that calling this method on a background thread does not guarantee that the latest
299     * value set will be received.
300     *
301     * @return the current value
302     */
303    @Nullable
304    public T getValue() {
305        // we do not return pending data here to be able to serve a consistent view to the main
306        // thread.
307        Object data = mData;
308        if (mData != NOT_SET) {
309            //noinspection unchecked
310            return (T) mData;
311        }
312        return null;
313    }
314
315    int getVersion() {
316        return mVersion;
317    }
318
319    /**
320     * Called when the number of active observers change to 1 from 0.
321     * <p>
322     * This callback can be used to know that this LiveData is being used thus should be kept
323     * up to date.
324     */
325    protected void onActive() {
326
327    }
328
329    /**
330     * Called when the number of active observers change from 1 to 0.
331     * <p>
332     * This does not mean that there are no observers left, there may still be observers but their
333     * lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}
334     * (like an Activity in the back stack).
335     * <p>
336     * You can check if there are observers via {@link #hasObservers()}.
337     */
338    protected void onInactive() {
339
340    }
341
342    /**
343     * Returns true if this LiveData has observers.
344     *
345     * @return true if this LiveData has observers
346     */
347    public boolean hasObservers() {
348        return mObservers.size() > 0;
349    }
350
351    /**
352     * Returns true if this LiveData has active observers.
353     *
354     * @return true if this LiveData has active observers
355     */
356    public boolean hasActiveObservers() {
357        return mActiveCount > 0;
358    }
359
360    class LifecycleBoundObserver implements LifecycleObserver {
361        public final LifecycleOwner owner;
362        public final Observer<T> observer;
363        public boolean active;
364        public int lastVersion = START_VERSION;
365
366        LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
367            this.owner = owner;
368            this.observer = observer;
369        }
370
371        @SuppressWarnings("unused")
372        @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
373        void onStateChange() {
374            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
375                removeObserver(observer);
376                return;
377            }
378            // immediately set active state, so we'd never dispatch anything to inactive
379            // owner
380            activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
381
382        }
383
384        void activeStateChanged(boolean newActive) {
385            if (newActive == active) {
386                return;
387            }
388            active = newActive;
389            boolean wasInactive = LiveData.this.mActiveCount == 0;
390            LiveData.this.mActiveCount += active ? 1 : -1;
391            if (wasInactive && active) {
392                onActive();
393            }
394            if (LiveData.this.mActiveCount == 0 && !active) {
395                onInactive();
396            }
397            if (active) {
398                dispatchingValue(this);
399            }
400        }
401    }
402
403    static boolean isActiveState(State state) {
404        return state.isAtLeast(STARTED);
405    }
406
407    private void assertMainThread(String methodName) {
408        if (!AppToolkitTaskExecutor.getInstance().isMainThread()) {
409            throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
410                    + " thread");
411        }
412    }
413}
414