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