Detailed explanation of the working principle of LiveData in advanced Android source code

Detailed explanation of the working principle of LiveData in advanced Android source code

[[422179]]

This article is reprinted from the WeChat public account "Android Development Programming", the author is Android Development Programming. Please contact the Android Development Programming public account for reprinting this article.

Preface

LiveData is an observable data storage class. Unlike regular observable classes, LiveData is lifecycle-aware, meaning it follows the lifecycle of other application components (such as Activity, Fragment, or Service).

This awareness ensures that LiveData updates only app component observers that are in an active lifecycle state.

LiveData itself is an observer, observing the Lifecycle of the component, and is also an observer. When the data changes, the observer of the data must be notified.

Earlier we explained the implementation principle of Lifecycle. Today we will look at LiveData.

Source code advanced analysis of the lifecycle component principle

1. Application of LiveData in App

1. Steps to use LiveData

  • Create an instance of LiveData to hold some type of data. This is usually done inside your ViewModel class;
  • Create an Observer object that defines an onChanged() method that controls what happens when the data held by the LiveData object changes. You typically create Observer objects in UI controllers, such as activities or fragments;
  • Use this method to attach an Observer object to an object. The method requires an object. This subscribes the object to the object so that it is notified of changes. You typically attach this object to a UI controller, such as an activity or fragment;

2. Simple example of livedata

Defining live data in viewModel

  1. public class NameViewModel extends ViewModel {
  2. // Create a LiveData with a String
  3. private MutableLiveData<String> mCurrentName;
  4. public MutableLiveData<String> getCurrentName() {
  5. if (mCurrentName == null ) {
  6. mCurrentName = new MutableLiveData<String>();
  7. }
  8. return mCurrentName;
  9. }
  10. // Rest of the ViewModel...
  11. }

Implement monitoring in activity or fragment to update UI

  1. public class NameActivity extends AppCompatActivity {
  2. private NameViewModel mModel;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. // Other code to setup the activity...
  7. // Get the ViewModel.
  8. mModel = ViewModelProviders. of (this).get(NameViewModel.class);
  9. // Create the observer which updates the UI.
  10. final Observer<String> nameObserver = new Observer<String>() {
  11. @Override
  12. public void onChanged(@Nullable final String newName) {
  13. // Update the UI, in this case , a TextView.
  14. mNameTextView.setText(newName);
  15. //Note that this is not written like this in MVVM. This is just a single line binding
  16. }
  17. };
  18. // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
  19. mModel.getCurrentName().observe(this, nameObserver);
  20. //Note that this place does not use Java8 lambda expressions, which can be written more concisely.
  21. }
  22. }

Update LiveData Object

Calling setValue(T) in the example causes the observer to call onChanged() with that value to refresh its UI. The example shows a button press, but setValue() or postValue() can be called to update mName for a variety of reasons, including in response to a network request or database load completion; in all cases, calling setValue() or postValue() triggers the observer and updates the UI;

  1. mButton.setOnClickListener(new OnClickListener() {
  2. @Override
  3. public void onClick( View v) {
  4. String anotherName = "John Doe" ;
  5. mModel.getCurrentName().setValue(anotherName);
  6. }
  7. });
  8. mButton.setOnClickListener(new OnClickListener() {
  9. @Override
  10. public void onClick( View v) {
  11. String anotherName = "John Doe" ;
  12. mModel.getCurrentName().setValue(anotherName);
  13. }
  14. });

2. Detailed analysis of the livedata principle

1. observe method

  • First of all, this method can only be registered for observation in the main thread;
  • The official document says that LiveData is only valid in the active life cycle, so we start to determine whether it is Lifecycle.Stete.DESTROYED. If it is, there is no more, just return directly;
  • The next step is to create LifecycleBoundObserver, where the lifecycle change logic is located;
  • Then the last line is to register the observer. If you want to know what Lifecycle.addObserver does, you can see the implementation principle of Android Lifecycle;
  1. // The key of the map is the LiveData data observer, and the value is the Lifecycle observer of the component
  2. private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
  3. new SafeIterableMap<>();
  4. @MainThread
  5. public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
  6. assertMainThread( "observe" );
  7. //Judge the current life cycle status
  8. if (owner.getLifecycle().getCurrentState() == DESTROYED) {
  9. // ignore  
  10. return ;
  11. }
  12. //Lifecycle life cycle change logic is here
  13. LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
  14. //mObservers save
  15. ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
  16. if (existing != null && !existing.isAttachedTo(owner)) {
  17. throw new IllegalArgumentException( "Cannot add the same observer"  
  18. + "with different lifecycles" );
  19. }
  20. if (existing != null ) {
  21. return ;
  22. }
  23. //Perceive the lifecycle life cycle changes
  24. owner.getLifecycle().addObserver(wrapper);
  25. }

2. LifecycleBoundObserver

  • LifecycleBoundObserver inherits ObserverWrapper and implements the LifecycleEventObserver interface.
  • ObserverWrapper is used to determine whether the component is currently active;
  • LifecycleEventObserver is the observer of Lifecycle. It overrides the onStateChanged method. When the Lifecycle life cycle changes, it will be received in this method, and LiveData will also be able to perceive it. If the component life cycle is DESTROYED, the observation will be removed.
  • At this point, LiveData has registered the component's lifecycle awareness and can start sending data;
  1. class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
  2. @NonNull
  3. //General components, such as Activity and fragment, can implement LifecycleOwner and get lifecycle
  4. final LifecycleOwner mOwner;
  5. LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
  6. super(observer);
  7. mOwner = owner;
  8. }
  9. /* Determine whether the current component is currently active */
  10. @Override
  11. boolean shouldBeActive() {
  12. return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
  13. }
  14. @Override
  15. public void onStateChanged(@NonNull LifecycleOwner source,
  16. @NonNull Lifecycle.Event event) {
  17. //Get the current life cycle status
  18. Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
  19. //If the component has been destroyed, remove the observation
  20. if (currentState == DESTROYED) {
  21. removeObserver(mObserver);
  22. return ;
  23. }
  24. Lifecycle.State prevState = null ;
  25. while (prevState != currentState) {
  26. prevState = currentState;
  27. activeStateChanged(shouldBeActive());
  28. currentState = mOwner.getLifecycle().getCurrentState();
  29. }
  30. }
  31. /* Determine whether to bind */
  32. @Override
  33. boolean isAttachedTo(LifecycleOwner owner) {
  34. return mOwner == owner;
  35. }
  36. /* Remove observation */
  37. @Override
  38. void detachObserver() {
  39. mOwner.getLifecycle().removeObserver(this);
  40. }
  41. }

3.activeStateChanged

There is a method in the ObserverWrapper class, which will be used in the sticky events later;

  1. void activeStateChanged(boolean newActive) {
  2. //If the component status does not change, return
  3. if (newActive == mActive) {
  4. return ;
  5. }
  6. // immediately set active state, so we'd never dispatch anything to inactive
  7. //owner
  8. mActive = newActive;
  9. changeActiveCounter(mActive ? 1 : -1);
  10. //If it is active, send data
  11. if (mActive) {
  12. dispatchingValue(this);
  13. }
  14. }

4. Send data 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 and analyze it step by step;

  1. protected void postValue(T value) {
  2. boolean postTask;
  3. //Lock
  4. synchronized (mDataLock) {
  5. postTask = mPendingData == NOT_SET;
  6. //Save the data value to be sent
  7. mPendingData = value;
  8. }
  9. if (!postTask) {
  10. return ;
  11. }
  12. //Send using the main thread Handler
  13. ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
  14. }

4.2. postValue is actually setValue

In the Runnable to be sent by postValue, you can see that the last line is the setValue method. The data is the mPendingData saved before, but newValue is given here;

  1. private final Runnable mPostValueRunnable = new Runnable() {
  2. @SuppressWarnings( "unchecked" )
  3. @Override
  4. public void run() {
  5. Object newValue;
  6. synchronized (mDataLock) {
  7. newValue = mPendingData;
  8. mPendingData = NOT_SET;
  9. }
  10. setValue((T) newValue);
  11. }
  12. };

4.3. postToMainThread main thread Handler

The postToMainThread method of the ArchTaskExecutor class is actually the postToMainThread method executed by the DefaultTaskExecutor class;

  1. public class ArchTaskExecutor extends TaskExecutor {
  2. @NonNull
  3. private TaskExecutor mDelegate;
  4. @NonNull
  5. private TaskExecutor mDefaultTaskExecutor;
  6. private ArchTaskExecutor() {
  7. mDefaultTaskExecutor = new DefaultTaskExecutor();
  8. mDelegate = mDefaultTaskExecutor;
  9. }
  10. ...
  11. @Override
  12. public void postToMainThread(Runnable runnable) {
  13. mDelegate.postToMainThread(runnable);
  14. }
  15. ...
  16. }

4.4. DefaultTaskExecutor class

We know it is DefaultTaskExecutor. postToMainThread, so we look at this method directly. Oops, it is too familiar code. Create Handler, pass in Looper.getMainLooper(), which is the main thread Handler, and then post the message;

  1. public class DefaultTaskExecutor extends TaskExecutor {
  2. @Nullable
  3. private volatile Handler mMainHandler;
  4. @Override
  5. public void postToMainThread(Runnable runnable) {
  6. if (mMainHandler == null ) {
  7. synchronized (mLock) {
  8. if (mMainHandler == null ) {
  9. mMainHandler = createAsync(Looper.getMainLooper());
  10. }
  11. }
  12. }
  13. //noinspection ConstantConditions
  14. mMainHandler.post(runnable);
  15. }
  16. }

4.5. setValue method

mVersion is assigned a value of -1 in the initialization constructor. The version number changes every time setValue is called.

setValue uses mData to save the value and then passes it to the dispatchingValue method for processing;

  1. @MainThread
  2. protected void setValue(T value) {
  3. assertMainThread( "setValue" );
  4. mVersion++;
  5. mData = value;
  6. dispatchingValue( null );
  7. }

4.6. dispatchingValue method

When setValue uses this method and the initiator passed is empty, it traverses the observers saved in mObservers to send data;

  1. @SuppressWarnings( "WeakerAccess" ) /* synthetic access */
  2. void dispatchingValue(@Nullable ObserverWrapper initiator) {
  3. if (mDispatchingValue) {
  4. mDispatchInvalidated = true ;
  5. return ;
  6. }
  7. mDispatchingValue = true ;
  8. do {
  9. mDispatchInvalidated = false ;
  10. if (initiator != null ) {
  11. //Sticky events, a single observer receives data
  12. considerNotify(initiator);
  13. initiator = null ;
  14. } else {
  15. //setvalue passed in initiator is empty, execute here
  16. // Take out mObservers and send data one by one
  17. for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
  18. mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
  19. considerNotify(iterator. next ().getValue());
  20. if (mDispatchInvalidated) {
  21. break;
  22. }
  23. }
  24. }
  25. } while (mDispatchInvalidated);
  26. mDispatchingValue = false ;
  27. }

4.7. considerNotify determines whether to send data

  • Here we first determine whether the component is active
  • In determining sticky events
  • Then use mVersion to determine whether the data has been sent
  • Finally, send data to the observer
  1. private void considerNotify(ObserverWrapper observer) {
  2. //Is the component active?
  3. if (!observer.mActive) {
  4. return ;
  5. }
  6. // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
  7. //
  8. // we still first   check observer.active to keep it as the entrance for events. So even if
  9. // the observer moved to an active state, if we've not received that event, we better not  
  10. // notify for a more predictable notification order .
  11. //Judge sticky events
  12. if (!observer.shouldBeActive()) {
  13. observer.activeStateChanged( false );
  14. return ;
  15. }
  16. // Check if data has been sent
  17. if (observer.mLastVersion >= mVersion) {
  18. return ;
  19. }
  20. observer.mLastVersion = mVersion;
  21. //Send data
  22. observer.mObserver.onChanged((T) mData);
  23. }

5. Sticky Event Analysis

  • A sticky event is to send data first, then register an observer and receive the message;
  • We start with LiveData.observe. The new page registers LiveData observer and Lifecycle observer.
  • When the life cycle of the new page changes, the LifecycleBoundObserver.onStateChanged method will be executed;
  • Next is the activeStateChanged method. Because it is a new page, the component state is active, so it goes to the dispatchingValue method.
  • If the dispatchingValue passed in is not empty, data will only be sent to the current observer.

Summarize:

1. When the LifecycleOwner lifecycle changes from inactive to active:

When Livedata adds an observer, it associates a LifecycleOwner, and then wraps the observer into a LifecycleBoundObserver, which is associated with the getLifecycle of LifecycleOwner. When the lifecycle of LifecycleBoundObserver changes, the onStateChanged method of LifecycleBoundObserver is called. In this method, it is determined whether the current lifecycle and the previous lifecycle are of the same type (either active or inactive). If so, it returns directly to avoid duplicate notifications (start notification, resume notification is not required). If they are not of the same type and the current lifecycle is active, the dispatchingValue method is called to notify the observer to determine whether LifecycleOwner is active. If so, the latest update held in Livedata is given to the observer;

2. When the value in Livedata changes:

Also call dispatchingValue to determine whether LifecycleOwner is active. If so, traverse and notify all observers;

<<:  Tencent applies to register WeChat Cloud trademark. Is payment for WeChat chat history coming?

>>:  Goodbye WeChat! My WeChat chat history is not worth 180 yuan

Recommend

12 ways to activate your social network

Many communities die soon after they are establis...

WeChat Moments 9-grid photo production_Taoduoduo

The usage is very simple. Just select the picture...

Tips for opening an account and promoting Baidu bidding in Guangzhou

How to promote new accounts is a question that ma...

Douyin store operation: Don’t cross these red line rules!

With the advent of the 5G era, ByteDance's qu...

2018 Tik Tok complete analysis report!

I haven't written anything seriously for a lo...

How to deploy new accounts in Juliang Qianchuan?

How can a new account conduct massive Qianchuan d...

The commonly eaten yellow throat is not actually the throat of a cow! It is...

This article was reviewed by Liu Shaowei, Deputy ...