preface
LiveData component is an observer-based message subscription/distribution component launched by Jetpack. It has host (Activity, Fragment) life-cycle awareness, which ensures that LiveData only distributes messages to active observers. Lifecycle will only receive the message if Lifecycle is in a DESTROYED state. Data listener behavior will be removed automatically if Lifecycle is in a DESTROYED state, thus avoiding common memory leaks and NPE issues.
The message distribution mechanism of LiveData is unmatched by Handler, EventBus and RxjavaBus. They will forward all the messages regardless of whether the current page is visible or not. The result is that the application does useless work to grab resources even when the background page is not visible
Google introduced LiveData
LiveData introduction project
implementation "Androidx. Lifecycle: lifecycle - livedata: 2.2.0." "
Copy the code
Introduction to LiveData core classes and methods
MutableLiveData
You need to use this subclass when you distribute messages using LiveData. The reason for this design is to take into account the single open and close principle, only get the MutableLiveData object can send messages, LiveData object can only receive messages, avoid the chaotic use of both sending and receiving messages when get the LiveData object (design principle: single responsibility)
@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
public MutableLiveData(T value) {
super(value);
}
public MutableLiveData(a) {
super(a); }@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value); }}Copy the code
MediatorLiveData
Data transmitted by multiple LiveData can be observed for unified processing. It can also serve as a LiveData observed by other observers
public class MediatorLiveData<T> extends MutableLiveData<T> {
privateSafeIterableMap<LiveData<? >, Source<? >> mSources =new SafeIterableMap<>();
// Add an observer
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
// Wrap the observed and observer as a Source object and add it to the map only once
Source<S> e = newSource<>(source, onChanged); Source<? > existing = mSources.putIfAbsent(source, e);if(existing ! =null&& existing.mObserver ! = onChanged) {throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if(existing ! =null) {
return;
}
// Check whether the page is active
if(hasActiveObservers()) { e.plug(); }}@MainThread // Remove observed and remove observer listening
public <S> void removeSource(@NonNull LiveData<S> toRemote) { Source<? > source = mSources.remove(toRemote);if(source ! =null) { source.unplug(); }}@CallSuper
@Override // Add observers to all sources
protected void onActive(a) {
for(Map.Entry<LiveData<? >, Source<? >> source : mSources) { source.getValue().plug(); }}@CallSuper
@Override // Active state removes observer listener for all Source
protected void onInactive(a) {
for(Map.Entry<LiveData<? >, Source<? >> source : mSources) { source.getValue().unplug(); }}private static class Source<V> implements Observer<V> {
final LiveData<V> mLiveData;
final Observer<? super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData<V> liveData, final Observer<? super V> observer) {
mLiveData = liveData;
mObserver = observer;
}
//liveData adds an observer
void plug(a) {
mLiveData.observeForever(this);
}
//liveData removes the observer
void unplug(a) {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if(mVersion ! = mLiveData.getVersion()) { mVersion = mLiveData.getVersion(); mObserver.onChanged(v); }}}Copy the code
Unified observation of data transmitted by multiple LiveData for unified processing
// Create two similar LiveData objects
LiveData<Integer> liveData1 = new MutableLiveData();
LiveData<Integer> liveData2 = new MutableLiveData();
// Create an aggregate class MediatorLiveData
MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
// Add the LiveData created above separately.
liveDataMerger.addSource(liveData1, observer);
liveDataMerger.addSource(liveData2, observer);
Observer observer = new Observer<Integer>() {
@Override
public void onChanged(@Nullable Integer s) {
titleTextView.setText(s);
}
// Once liveData1 or liveData2 sends new data, the Observer can observe it in order to process the UI update uniformly
Copy the code
Transformations
Transformations are used to perform Transformations of the LiveData class. Transformation methods can be used to convey information throughout the lifetime of the observer. The conversion is not evaluated unless the observer observes the returned LiveData object. Because transformations are computed late, lifecycle related behaviors are passed implicitly without the need for other explicit calls or dependencies
Google introduced Transformations
public class Transformations {
private Transformations(a) {}/**LiveData
userLiveData = ... ; *LiveData
userFullNameLiveData = *Transformations.map( * userLiveData, * user -> user.firstName + user.lastName); }) * /
// apply the given function on the main thread to each value emitted by the LiveData source,
// Then return LiveData to emit the result value. The given function func will be executed on the main thread
@MainThread
@NonNull
public static <X, Y> LiveData<Y> map(
@NonNull LiveData<X> source,
@NonNull final Function<X, Y> mapFunction) {
MapFunction returns a new value set to result
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) { result.setValue(mapFunction.apply(x)); }});return result;
}
@MainThread
@NonNull
public static <X, Y> LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction) {
// Listen for source changes to get the value of source
SwitchMapFunction returns a new LiveData and listens for the value of this LiveData to change set to result
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
LiveData<Y> newLiveData = switchMapFunction.apply(x);
if (mSource == newLiveData) {
return;
}
if(mSource ! =null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if(mSource ! =null) {
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) { result.setValue(y); }}); }}});return result;
}
@MainThread
@NonNull
public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
// outputLiveData onChanged is triggered only when a different value is changed
final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
outputLiveData.addSource(source, new Observer<X>() {
boolean mFirstTime = true;
@Override
public void onChanged(X currentValue) {
final X previousValue = outputLiveData.getValue();
if (mFirstTime
|| (previousValue == null&& currentValue ! =null) || (previousValue ! =null && !previousValue.equals(currentValue))) {
mFirstTime = false; outputLiveData.setValue(currentValue); }}});returnoutputLiveData; }}Copy the code
You can make a quick buck or a quick buck
class UserViewModel extends AndroidViewModel {
//mapLiveData<User> userLiveData = ... ;// Return a new LiveData
LiveData<String> userFullNameLiveData =
Transformations.map(
userLiveData,
user -> user.firstName + user.lastName);
});
//switchMap
MutableLiveData<String> nameQueryLiveData = ...
LiveData<List<String>> getUsersWithNameLiveData() {
return Transformations.switchMap(
nameQueryLiveData,
name -> myDataSource.getUsersWithNameLiveData(name));
}
void setNameQuery(String name) {
this.nameQueryLiveData.setValue(name); }}Copy the code
Analysis of implementation principle of LiveData event distribution
LiveData constructor
public abstract class LiveData<T> {
// Set of observers
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
// The event changes the counter
static final int START_VERSION = -1;
static final Object NOT_SET = new Object();
int mActiveCount = 0;
// Volatile keyword
private volatile Object mData;
private int mVersion;
volatile Object mPendingData = NOT_SET;
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;// The event counter increases by 1
}
public LiveData(a) { mData = NOT_SET; mVersion = START_VERSION; }}Copy the code
Observe Register with host life cycle, no need to register. (Deregister function is called when target life cycle is dead.)
@MainThread // Main thread limit
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// Assert that this method can only be called on the main thread, as can observeForever.
assertMainThread("observe");
// Get the state of the host
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// Wrap the registered observer as an observer with a living boundary
// It can listen for host destruction events, thus actively unregister itself, to avoid memory leaks
// Whether the observer is active equals whether the host is visible
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// Can be added only once
if(existing ! =null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if(existing ! =null) {
return;
}
// Add an observer to Lifecycle
owner.getLifecycle().addObserver(wrapper);
}
Copy the code
LifecycleBoundObserver
Observer object
If you look at the Lifecycle source code, you’ll see that the way observers are added is very similar
// Lifecycle event dispatch interface
public interface LifecycleEventObserver extends LifecycleObserver {
void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);/ / observer
mOwner = owner;
}
@Override
boolean shouldBeActive(a) {
// Observers registered using the Observer method are wrapped as LifecycleBoundObserver
// Whether the observer is active is equal to whether the host state is greater than or equal to STARTED,
// If the page is not currently visible and you send a message, it will not be distributed at this time, to avoid background tasks to grab resources, and will be distributed when the page is visible again.
// Note: If you use observerForever to register an observer,
// is wrapped as AlwaysActiveObserver, whose shouldBeActive returns true consistently. Data is received even if it is not visible on the page
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// In this case, if the listener detects that the host is destroyed, it actively removes itself from the Observer of LiveData
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);// Remove the observer from the map when the host is dead
return;
}
// Indicates that the state of the host has changed, at which point the host is determined to be active
activeStateChanged(shouldBeActive());
}
@Override // Check whether it is the same activity or fragment.
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver(a) {// This is where the observer is actually called when the map is removed
mOwner.getLifecycle().removeObserver(this); }}Copy the code
ObserverWrapper
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;//-1 Number of events that are separated
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive(a);
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver(a) {}// Host lifecycle changes called
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// Change the state of the observer
mActive = newActive;
// 0 when the observer is first added
boolean wasInactive = LiveData.this.mActiveCount == 0;
OnActive is triggered if there is only one active observer
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
// Some initialization can be done
onActive();
}
// Trigger onInactive if there are no active observers
// When this method is triggered, you can do many things, such as resource release, etc
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
// If the observer is active, the data will be distributed
// Notice that this = observer is passed
if (mActive) {
dispatchingValue(this); }}}Copy the code
dispatchingValue
Data distribution process control
//2 Boolean values similar to the flag switch are used to control the sequence control during data distribution
private boolean mDispatchingValue;
private boolean mDispatchInvalidated;
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {//true indicates that the following data distribution process is not complete
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if(initiator ! =null) {
// The first time the registered observation data is synchronized to the observer
// The distribution of sticky events allows the first data to receive new data as well as the registered observer
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// Non-first-time registered observation data is delivered synchronously to the observer
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {// true indicates that new data is coming and this data delivery is meaningless
break; // End the loop}}}}while (mDispatchInvalidated); // Execute
mDispatchingValue = false;
}
Copy the code
Notice where the data is actually distributed, three conditions need to be met
private void considerNotify(ObserverWrapper observer) {
// The host is not active
if(! observer.mActive) {return;
}
// Determine that the host is not active and change the observer state to false
if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
return;
}
// The number of times the observer receives the message is greater than or equal to the number of times the observer sends the message
// But when the Observer was created, verison=-1
// If LiveData has already sent data. This is not enough, there is a sticky event, the post-registered observer received the message sent earlier.
// mLastVersion =-1 mVersion=1 ||>1
if (observer.mLastVersion >= mVersion) {
return;
}
// Each time a message is distributed, the version of the observer is aligned with that of the LiveData to prevent repeated transmission
observer.mLastVersion = mVersion;
// Final data transfer
observer.mObserver.onChanged((T) mData);
}
Copy the code
SetValue/postValue Event updates are sent
// Can be executed on child threads
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if(! postTask) {return;
}
// The underlying execution is thrown back to the main thread via handler.post (runnable)
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
// must be executed on the main thread
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
// Event distribution
dispatchingValue(null);
}
Copy the code
Other observer: The AlwaysActiveObserver does not care about the host lifecycle state. It can observe data changes at any time. The key is that shouldBeActive returns true at all times and does not check the host lifecycle state
// Regardless of the host lifecycle state, data changes can be observed at any time
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
// Add to observer union
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);
}
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer<? super T> observer) {
super(observer);
}
@Override// Always return true
boolean shouldBeActive(a) {
return true; }}// Remove the observer
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
removed.detachObserver();// Remove the host's lifecycle observer
removed.activeStateChanged(false); // Call onInactive () to do some resource release work
}
// Remove all observers from this host
@SuppressWarnings("WeakerAccess")
@MainThread
public void removeObservers(@NonNull final LifecycleOwner owner) {
assertMainThread("removeObservers");
for (Map.Entry<Observer<? super T>, ObserverWrapper> entry : mObservers) {
if(entry.getValue().isAttachedTo(owner)) { removeObserver(entry.getKey()); }}}Copy the code
LiveData summary
-
Ensure that the interface conforms to data state LiveData follows observer mode. When the life cycle state changes, LiveData notifies the [Observer] object and sends it the latest data. Instead of updating the interface every time the data changes, the observer can update the interface whenever it receives an onChanged event. (Distribution has logic to check the lifecycle of the host activity.)
-
You don’t need to manually handle the life cycle, you just need to observe the relevant data, you don’t need to manually stop or resume the observation. LiveData automatically manages unregistration of the Observer because it senses changes in the host life cycle and automatically unregisters onDestory in the host life cycle. Therefore, using LiveData for message distribution does not cause memory leaks
-
Data is always up to date. If the host’s life cycle becomes inactive, it receives the latest data when it becomes active again. For example, an Activity that was once in the background receives the latest data as soon as it returns to the foreground.
-
Stickiness event distribution is supported by sending a piece of data and then registering an observer, which by default will receive the previously sent data
-
Shared resources can extend LiveData using the singleton pattern to implement a global message distribution bus. Meituan LiveEventBus