1. Know LiveData
- Google LiveData overview
- LiveData is an observable data store class. Unlike regular observable classes, LiveData has lifecycle awareness, meaning that it follows the lifecycle of other application components such as activities, fragments, or services. This awareness ensures that LiveData updates only application component observers that are in an active lifecycle state.
- LiveData itself is the observer, Lifecycle of the observing component is also the observed, the observer of the data is notified when the data changes.
- Understanding Android Lifecycle will help you understand LiveData.
2. Easy to use
- So we first add MutableLiveData to our ViewModel, and when we get a network request, or a database or something like that back, if we want to send data, we can use postValue, and this method will use Handler to switch to the main thread and setValue, and if it’s already on the main thread, I’m just going to use the normal setValue method.
class TestViewModel : ViewModel(a){
val livedata = MutableLiveData<String>("bao")
init {
livedata.postValue("bao_post")}fun initData(a){
livedata.value = "bao_set"}}Copy the code
/* Observe data changes */
viewModel.livedata.observe(TestFragment@this, object : Observer<String> {
override fun onChanged(t: String?) { t? .let { Log.d(TAG,it) } } })Copy the code
3. Analyze component Lifecycle perception
3.1 observe method
- First, this method can only be observed on the main thread.
- The official document says LiveData is valid only in the life cycle, so the start first determine whether for Lifecycle. Stete. DESTROYED, so there is no then, return directly.
- The next step is to create the LifecycleBoundObserver, where the lifecycle change logic resides.
- And then the last line of registration observation, if you want to see what Lifecycle. AddObserver does look at the Implementation of Android Lifecycle.
// Map key is the LiveData data observer and value is the component's Lifecycle observer
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
// Determine the state of the current lifecycle
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//Lifecycle changes logic here
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
/ / mObservers preservation
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if(existing ! =null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if(existing ! =null) {
return;
}
// Perceive lifecycle changes
owner.getLifecycle().addObserver(wrapper);
}
Copy the code
3.2 What does LifecycleBoundObserver do
- LifecycleBoundObserver inherits ObserverWrapper and implements the LifecycleEventObserver interface.
- ObserverWrapper is used to determine whether the component is currently active.
- LifecycleEventObserver is Lifecycle observer, override onStateChanged method, as Lifecycle Lifecycle Lifecycle changes will be received in this method, LiveData will know, Remove observation if the component’s lifecycle is DESTROYED.
- At this point LiveData is registered with the component’s life-cycle awareness and can start sending data.
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
// General components such as activities and fragments can implement LifecycleOwner, which can be carried out lifecycle
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
/* Determine whether the current component is currently active */
@Override
boolean shouldBeActive(a) {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// Get the current life cycle state
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
// If the component has been destroyed, remove the observation
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
/* Determines whether to bind */
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
/* Remove observation */
@Override
void detachObserver(a) {
mOwner.getLifecycle().removeObserver(this); }}Copy the code
3.3 activeStateChanged method
- There’s a method inside the ObserverWrapper class that’s going to be used for stickiness events, so let’s take a look at that.
void activeStateChanged(boolean newActive) {
// Returns if the component status is unchanged
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
// If it is active, send data
if (mActive) {
dispatchingValue(this); }}Copy the code
3.4 Simple Flow Chart
4. Send data for analysis
4.1 postValue Sends data to the main thread
- The core of this method is to use the main thread Handler to send data, step by step analysis, see how it is written.
protected void postValue(T value) {
boolean postTask;
/ / lock
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
// Save the data value to be sent
mPendingData = value;
}
if(! postTask) {return;
}
// Send using the main thread Handler
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
Copy the code
4.2 postValue is actually setValue
- In the Runnable that postValue is going to send, you can see that the last line is the setValue method, and the data is the mPendingData that was saved before, but is given to newValue here.
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run(a) {
Object newValue;
synchronized(mDataLock) { newValue = mPendingData; mPendingData = NOT_SET; } setValue((T) newValue); }};Copy the code
4.3 postToMainThread Main thread Handler
- The postToMainThread method of the ArchTaskExecutor class is executed by DefaultTaskExecutor.
public class ArchTaskExecutor extends TaskExecutor {
@NonNull
private TaskExecutor mDelegate;
@NonNull
private TaskExecutor mDefaultTaskExecutor;
private ArchTaskExecutor(a) {
mDefaultTaskExecutor = newDefaultTaskExecutor(); mDelegate = mDefaultTaskExecutor; }...@Override
public void postToMainThread(Runnable runnable) { mDelegate.postToMainThread(runnable); }... }Copy the code
4.4 DefaultTaskExecutor class
- Defaulttaskexecutor.posttomainthread, so let’s just look at this method, oops, familiar code, create Handler, and pass in looper.getMainLooper (), It’s the main thread Handler, and then it posts the message.
- If you are not familiar with Handler, you can see the Android Handler from use to advanced.
public class DefaultTaskExecutor extends TaskExecutor {
@Nullable
private volatile Handler mMainHandler;
@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) { mMainHandler = createAsync(Looper.getMainLooper()); }}}//noinspection ConstantConditionsmMainHandler.post(runnable); }}Copy the code
4.5 the setValue method
- MVersion is set to -1 in the initialization constructor, and the version number changes each time you setValue.
- So setValue is just saving the value with mData and handing it to the dispatchingValue method.
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
Copy the code
4.6 dispatchingValue method
- SetValue if the initiator is empty, the observer sent data is iterated through the mObservers.
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if(initiator ! =null) {
// Sticky events accept data for a single observer
considerNotify(initiator);
initiator = null;
} else {
// SetValue indicates that the initiator is empty
// Take out the mObservers and send data one by one
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break; }}}}while (mDispatchInvalidated);
mDispatchingValue = false;
}
Copy the code
4.7 Termination of Duties by Notice
- Here, check whether the component is active.
- In judging sticky events.
- Then use mVersion to determine whether the data was sent.
- Finally, the data is sent to the observer.
private void considerNotify(ObserverWrapper observer) {
// Whether the component is active
if(! observer.mActive) {return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
// Determine stickiness events
if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
return;
}
// Verify whether data has been sent
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// Send data
observer.mObserver.onChanged((T) mData);
}
Copy the code
4.8 Simple Flow Chart
5. Sticky events
5.1 Stickiness event analysis
1. Sticky events are those that send data first, then register observers and receive messages. 2. We will start with LiveData. Observe and register LiveData observer on the new page. 3. When the new page life cycle change, will be executed LifecycleBoundObserver. OnStateChanged method, go home to see 3.2 if forgotten. 4. Next activeStateChanged method, because it is a new page and the component state is active, go to dispatchingValue method, see 3.3. The initiator of the current new page is not empty, so it will only send data to the current observer.