LiveData.java revision c43ce90b803cdfa033dfc94fa4161371ed6f3ec6
1/*
2 * Copyright (C) 2016 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 com.android.support.lifecycle;
18
19import static com.android.support.lifecycle.Lifecycle.DESTROYED;
20import static com.android.support.lifecycle.Lifecycle.STARTED;
21
22import android.support.annotation.MainThread;
23import android.support.annotation.Nullable;
24
25import com.android.support.apptoolkit.internal.SafeIterableMap;
26import com.android.support.executors.AppToolkitTaskExecutor;
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 LifecycleProvider}, and
34 * this observer will be notified about modifications of the wrapped data only if the paired
35 * LifecycleProvider is in active state. LifecycleProvider is considered as active, if its state is
36 * {@link Lifecycle#STARTED} or {@link Lifecycle#RESUMED}. An observer added without a
37 * LifecycleProvider is considered as always active and thus will be always notified about
38 * modifications. For those observers, you should manually call {@link #removeObserver(Observer)}.
39 *
40 * <p> An observer added with a Lifecycle will be automatically removed if the corresponding
41 * Lifecycle moves to {@link Lifecycle#DESTROYED} state. This is especially useful for activities
42 * and fragments where they can safely observe LiveData and not worry about leaks: they will be
43 * instantly unsubscribed when they are destroyed.
44 *
45 * <p>
46 * In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods
47 * to get notified when number of active {@link Observer}s change between 0 and 1.
48 * This allows LiveData to release any heavy resources when it does not have any Observers that
49 * are actively observing.
50 * <p>
51 * This class is designed to hold individual data fields of {@link ViewModel},
52 * but can also be used for sharing data between different modules in your application
53 * in a decoupled fashion.
54 *
55 * @param <T> The type of data hold by this instance
56 * @see ViewModel
57 */
58@SuppressWarnings({"WeakerAccess", "unused"})
59// TODO: Thread checks are too strict right now, we may consider automatically moving them to main
60// thread.
61public class LiveData<T> {
62    private final Object mDataLock = new Object();
63    private static final int START_VERSION = -1;
64    private static final Object NOT_SET = new Object();
65
66    private static final LifecycleProvider ALWAYS_ON = new LifecycleProvider() {
67
68        private LifecycleRegistry mRegistry = init();
69
70        private LifecycleRegistry init() {
71            LifecycleRegistry registry = new LifecycleRegistry(this);
72            registry.handleLifecycleEvent(Lifecycle.ON_CREATE);
73            registry.handleLifecycleEvent(Lifecycle.ON_START);
74            registry.handleLifecycleEvent(Lifecycle.ON_RESUME);
75            return registry;
76        }
77
78        @Override
79        public Lifecycle getLifecycle() {
80            return mRegistry;
81        }
82    };
83
84    private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
85            new SafeIterableMap<>();
86
87    // how many observers are in active state
88    private int mActiveCount = 0;
89    private Object mData = NOT_SET;
90    // when setData is called, we set the pending data and actual data swap happens on the main
91    // thread
92    private volatile Object mPendingData = NOT_SET;
93    private int mVersion = START_VERSION;
94
95    private boolean mDispatchingValue;
96    @SuppressWarnings("FieldCanBeLocal")
97    private boolean mDispatchInvalidated;
98
99    private void considerNotify(LifecycleBoundObserver observer) {
100        if (!observer.active) {
101            return;
102        }
103        if (observer.lastVersion >= mVersion) {
104            return;
105        }
106        observer.lastVersion = mVersion;
107        //noinspection unchecked
108        observer.observer.onChanged((T) mData);
109    }
110
111    private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
112        if (mDispatchingValue) {
113            mDispatchInvalidated = true;
114            return;
115        }
116        mDispatchingValue = true;
117        do {
118            mDispatchInvalidated = false;
119            if (initiator != null) {
120                considerNotify(initiator);
121                initiator = null;
122            } else {
123                for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
124                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
125                    considerNotify(iterator.next().getValue());
126                    if (mDispatchInvalidated) {
127                        break;
128                    }
129                }
130            }
131        } while (mDispatchInvalidated);
132        mDispatchingValue = false;
133    }
134
135    /**
136     * Adds the given observer to the observers list within the lifespan of the given provider. The
137     * events are dispatched on the main thread. If LiveData already has data set, it will be
138     * delivered to the observer.
139     * <p>
140     * The observer will only receive events if the provider is in {@link Lifecycle#STARTED} or
141     * {@link Lifecycle#RESUMED} state (active).
142     * <p>
143     * If the provider moves to the {@link Lifecycle#DESTROYED} state, the observer will
144     * automatically be removed.
145     * <p>
146     * When data changes while the {@code provider} is not active, it will not receive any updates.
147     * If it becomes active again, it will receive the last available data automatically.
148     * <p>
149     * LiveData keeps a strong reference to the observer and the provider as long as the given
150     * LifecycleProvider is not destroyed. When it is destroyed, LiveData removes references to
151     * the observer & the provider.
152     * <p>
153     * If the given provider is already in {@link Lifecycle#DESTROYED} state, LiveData ignores the
154     * call.
155     * <p>
156     * If the given provider, observer tuple is already in the list, the call is ignored.
157     * If the observer is already in the list with another provider, LiveData throws an
158     * {@link IllegalArgumentException}.
159     *
160     * @param provider The LifecycleProvider which controls the observer
161     * @param observer The observer that will receive the events
162     */
163    @MainThread
164    public void observe(LifecycleProvider provider, Observer<T> observer) {
165        if (provider.getLifecycle().getCurrentState() == DESTROYED) {
166            // ignore
167            return;
168        }
169        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(provider, observer);
170        LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
171        if (existing != null && existing.provider != wrapper.provider) {
172            throw new IllegalArgumentException("Cannot add the same observer"
173                    + " with different lifecycles");
174        }
175        if (existing != null) {
176            return;
177        }
178        provider.getLifecycle().addObserver(wrapper);
179        wrapper.activeStateChanged(isActiveState(provider.getLifecycle().getCurrentState()));
180    }
181
182    /**
183     * Adds the given observer to the observers list. This call is similar to
184     * {@link LiveData#observe(LifecycleProvider, Observer)} with a LifecycleProvider, which
185     * is always active. This means that the given observer will receive all events and will never
186     * be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop
187     * observing this LiveData.
188     * While LiveData has one of such observers, it will be considered
189     * as active.
190     * <p>
191     * If the observer was already added with a provider to this LiveData, LiveData throws an
192     * {@link IllegalArgumentException}.
193     *
194     * @param observer The observer that will receive the events
195     */
196    @MainThread
197    public void observe(Observer<T> observer) {
198        observe(ALWAYS_ON, observer);
199    }
200
201    /**
202     * Removes the given observer from the observers list.
203     *
204     * @param observer The Observer to receive events.
205     */
206    @MainThread
207    public void removeObserver(final Observer<T> observer) {
208        assertMainThread("removeObserver");
209        LifecycleBoundObserver removed = mObservers.remove(observer);
210        if (removed == null) {
211            return;
212        }
213        removed.provider.getLifecycle().removeObserver(removed);
214        removed.activeStateChanged(false);
215    }
216
217    /**
218     * Removes all observers that are tied to the given LifecycleProvider.
219     *
220     * @param provider The provider scope for the observers to be removed.
221     */
222    @MainThread
223    public void removeObservers(final LifecycleProvider provider) {
224        assertMainThread("removeObservers");
225        for (Map.Entry<Observer<T>, LifecycleBoundObserver> entry : mObservers) {
226            if (entry.getValue().provider == provider) {
227                removeObserver(entry.getKey());
228            }
229        }
230    }
231
232    /**
233     * Sets the value. If there are active observers, the value will be dispatched to them.
234     * <p>
235     * If this method is called on a background thread, the call will be forwarded to the main
236     * thread so calling {@link #getValue()} right after calling {@code setValue} may
237     * not return the value that was set.
238     *
239     * @param value The new value
240     */
241    public void setValue(T value) {
242        // we keep it in pending data so that last set data wins (e.g. we won't be in a case where
243        // data is set on the main thread at a later time is overridden by data that was set on a
244        // background thread.
245        synchronized (mDataLock) {
246            mPendingData = value;
247        }
248        AppToolkitTaskExecutor.getInstance().executeOnMainThread(new Runnable() {
249            @Override
250            public void run() {
251                synchronized (mDataLock) {
252                    if (mPendingData != NOT_SET) {
253                        mVersion++;
254                        mData = mPendingData;
255                        mPendingData = NOT_SET;
256                    }
257                }
258                dispatchingValue(null);
259            }
260        });
261    }
262
263    /**
264     * Returns the current value.
265     * Note that calling this method on a background thread does not guarantee that the latest
266     * value set will be received.
267     *
268     * @return the current value
269     */
270    @Nullable
271    public T getValue() {
272        // we do not return pending data here to be able to serve a consistent view to the main
273        // thread.
274        Object data = mData;
275        if (mData != NOT_SET) {
276            //noinspection unchecked
277            return (T) mData;
278        }
279        return null;
280    }
281
282    /**
283     * Called when the number of active observers change to 1 from 0.
284     * <p>
285     * This callback can be used to know that this LiveData is being used thus should be kept
286     * up to date.
287     */
288    protected void onActive() {
289
290    }
291
292    /**
293     * Called when the number of active observers change from 1 to 0.
294     * <p>
295     * This does not mean that there are no observers left, there may still be observers but their
296     * lifecycle states is not {@link Lifecycle#STARTED} or {@link Lifecycle#STOPPED} (like an
297     * Activity in the back stack).
298     * <p>
299     * You can get the number of observers via {@link #getObserverCount()}.
300     */
301    protected void onInactive() {
302
303    }
304
305    /**
306     * Returns the number of observers.
307     * <p>
308     * If called on a background thread, the value might be unreliable.
309     *
310     * @return The number of observers
311     */
312    public int getObserverCount() {
313        return mObservers.size();
314    }
315
316    /**
317     * Returns the number of active observers.
318     * <p>
319     * If called on a background thread, the value might be unreliable.
320     *
321     * @return The number of active observers
322     */
323    public int getActiveObserverCount() {
324        return mActiveCount;
325    }
326
327    class LifecycleBoundObserver implements LifecycleObserver {
328        public final LifecycleProvider provider;
329        public final Observer<T> observer;
330        public boolean active;
331        public int lastVersion = START_VERSION;
332
333        LifecycleBoundObserver(LifecycleProvider provider, Observer<T> observer) {
334            this.provider = provider;
335            this.observer = observer;
336        }
337
338        @SuppressWarnings("unused")
339        @OnLifecycleEvent(Lifecycle.ANY)
340        void onStateChange() {
341            if (provider.getLifecycle().getCurrentState() == DESTROYED) {
342                removeObserver(observer);
343                return;
344            }
345            // immediately set active state, so we'd never dispatch anything to inactive provider
346            activeStateChanged(isActiveState(provider.getLifecycle().getCurrentState()));
347
348        }
349
350        void activeStateChanged(boolean newActive) {
351            if (newActive == active) {
352                return;
353            }
354            active = newActive;
355            boolean inInActive = mActiveCount == 0;
356            mActiveCount += active ? 1 : -1;
357            if (inInActive && active) {
358                onActive();
359            }
360            if (!inInActive && !active) {
361                onInactive();
362            }
363            if (active) {
364                dispatchingValue(this);
365            }
366        }
367    }
368
369    static boolean isActiveState(@Lifecycle.State int state) {
370        return state >= STARTED;
371    }
372
373    private void assertMainThread(String methodName) {
374        if (!AppToolkitTaskExecutor.getInstance().isMainThread()) {
375            throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
376                    + " thread. You can easily move the call to main thread using"
377                    + " AppToolkitTaskExecutor.");
378        }
379    }
380}
381