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 & 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