LiveData source code analysis (Part 1)

Posted by jerk on Tue, 08 Feb 2022 09:05:27 +0100

LiveData source code analysis (Part 1)

##What is LiveData

Official document explanation link: https://developer.android.google.cn/reference/androidx/lifecycle/LiveData?hl=en

LiveData is a class of data holders that can be observed in a given life cycle. This means that the Observer can add a lifecycle owner in pairs with the, and only when the paired lifecycle owner is active will the Observer be notified of the modification of the packaging data. If the lifecycle owner status is lifecycle State. Started or, it is considered active lifecycle State. RESUMED. The via Observer (Observer) added by the Observer is considered to be always active, so you will always be notified of the modification. For these observers, you should manually call removeObserver(Observer).

If the corresponding lifecycle is moved to lifecycle State. If the state is destroyed, the observer added with the life cycle will be automatically deleted. This is particularly useful for activities and fragments that can safely observe LiveData without worrying about leaks: when they are destroyed, they will be immediately unsubscribed.

In addition, when the number of active changes between 0 and 1, LiveData has onActive() and onInactive() notification methods Observer. This allows LiveData to free up a lot of resources without any active observers.

This class is designed to accommodate a single data field ViewModel, but can also be used to share data between different modules in an application in a separate manner

Advantages of using LiveData

Using LiveData has the following advantages (reference Android development documents):

  • Ensure that the interface complies with the data status

    LiveData follows the observer mode. LiveData notifies when the lifecycle state changes Observer Object. You can integrate the code to update the interface in these Observer objects. It can happen every time the interface changes, not every time the Observer updates the interface.

  • No memory leaks

    The observer is bound to Lifecycle Object and clean itself up after its associated lifecycle is destroyed.

  • No crash due to Activity stop

    If the observer's lifecycle is inactive (such as returning an Activity in the stack), it will not receive any LiveData events.

  • You no longer need to manually process the lifecycle

    The interface component only observes relevant data and will not stop or resume observation. LiveData will automatically manage all these operations because it can perceive the relevant lifecycle state changes when observing.

  • Data is always up-to-date

    If the lifecycle becomes inactive, it receives the latest data when it becomes active again. For example, an Activity that used to be in the background will receive the latest data immediately after returning to the foreground.

  • Appropriate configuration changes

    If an Activity or Fragment is recreated due to configuration changes, such as device rotation, it will immediately receive the latest available data.

  • shared resource

    You can extend using single instance mode LiveData Object to encapsulate system services so that they can be shared in applications. The LiveData object is connected to the system service once, and then any observer who needs the corresponding resources only needs to observe the LiveData object. For details, see Extend LiveData.

For example: to process the login result, you need to check the current status of the activity first

public void login(){
  String name="";
  String password="";
  loginModel.login(name,password,new Callback<User>(){
    public void onSuccess(User user){
      if(!isDestroyed()){
        //todo handles the business of successful login
      }
    }
    public void onFailure(int code,String msg){
      if(!isDestroyed()){
        //todo handles the business of login failure
      }
    }
  })
}

Cancel asynchronous task in activity

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Override
  	protected void onDestroy(){
      super.onDestroy();
      mainModel.cancelTask()
    }
}

API usage

The main categories are

The official documents are provided. Students who can't use LiveData can learn from the official documents first

https://developer.android.com/topic/libraries/architecture/livedata#java

Let's analyze the source code of LiveData

First of all, let's analyze the calling process of LiveData setValue source code we often use

//setValue notifies the observer of data changes
protected void setValue(T value) {
	//Is check the main thread
    assertMainThread("setValue");
    //Mark current version number
    mVersion++;
    //Cache current notification data
    mData = value;
    //send data
    dispatchingValue(null);
}

Analysis of dispatchingValue method

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
        	//If sending ends, the current sending flag is invalid
            mDispatchInvalidated = true;
            return;
        }
        //Mark distributing data
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
            	//loop all observers send data
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions();
                 iterator.hasNext(); ) {
                 	//Resolve notification
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        //Mark the sending status as false and the sending task ends
        mDispatchingValue = false;
 }

considerNotify method analysis

private void considerNotify(ObserverWrapper observer) {
 	//Check whether the observer is active
    if (!observer.mActive) {
    	//The inactive state ends sending data to this observer
        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.
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    //If the last data version received by the observer is greater than or equal to the version of the currently transmitted data, the transmission of the currently transmitted data for this observation will be ended
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    //Update the number of last versions received by this observer
    observer.mLastVersion = mVersion;
    //The object of the observer is decorated to get the observer object and then the onChanged is notified to change the data. This onChanged method is the callback method of the upper level LiveData that registers a Observer observer interface.
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
 }

Let's analyze the calling process of LiveData observe

 
 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
 	//Must be registered in the main thread
     assertMainThread("observe");
     if (owner.getLifecycle().getCurrentState() == DESTROYED) {
         // If the current life cycle of ignore is in the status of destruction, the registration ends
         return;
     }
     //Wrap an observer with the ability to perceive the life cycle through lifecycle owner and observer
     LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
     //check whether the current observer is registered
     ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
     if (existing != null && !existing.isAttachedTo(owner)) {
     	//Exception thrown if registered
         throw new IllegalArgumentException("Cannot add the same observer"
                 + " with different lifecycles");
     }
     if (existing != null) {
         return;
     }
     //Register the wrapped observer in componentactivity lifecycle registry mobservermap
     owner.getLifecycle().addObserver(wrapper);
 }

Observer of life cycle capability packaging class LifecycleBoundObserver

    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
          //State. STARTED State. Returned true
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
          //Callback when lifecycle changes
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
              //Destroy status unsubscribe 
                removeObserver(mObserver);
                return;
            }
          //Notification activation status changed
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

To summarize

  1. The observer is packaged as a lifecycle bound observer wrapper
  2. The Lifecycle mObserverMap of the lifecycle owner is added to the wrapper
  3. When the lifecycle changes, the onStateChanged method of LifecycleBoundObserver will be notified

Analysis of activeStateChanged method of ObserverWrapper

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    // immediately set active state, so we'd never dispatch anything to inactive
    // owner
    mActive = newActive;
  //Mark whether it is currently active
    boolean wasInactive = LiveData.this.mActiveCount == 0;
  //Calculate the number of currently active LiveData observers active + 1 inactive - 1
    LiveData.this.mActiveCount += mActive ? 1 : -1;
  //LiveData is inactive and the current activity is activated
    if (wasInactive && mActive) {
      //LiveData is activated and called
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
      //LiveData inactive call
        onInactive();
    }

    if (mActive) {
      //Resend data when activated
        dispatchingValue(this);
    }
}

To summarize, after receiving the life cycle change notification, I did the following things

  1. LiveData is activated and onActive is called to notify the business layer that LiveData has been observed
  2. When LiveData becomes inactive, call onInactive to notify the business layer that LiveData is idle and some resources can be released
  3. The observer is activated, and the data in LiveData will be notified again

summary

  1. LiveData is an implementation based on observer mode
  2. Only when the observer is active (Lifecycle State.STARTED | State.RESUMED) can he receive the message sent by LiveData
  3. After activity fragment deleted, the observer will not receive a message. The asynchronous task notifies the UI update through LiveData and does not need to pay attention to the UI status
  4. Call setValue. If the current observer is inactive, the data will be temporarily stored. When the observer is set to the active state, the observer will be automatically notified of data update

Some scenarios that LiveData can solve

###Scenario 1 topic details like topic comments

Page A (topic list)

  1. Display: number of item likes
  2. Display: item like status

Page B (topic details)

  1. Display: number of likes
  2. Display: like status
  3. Behavior: like

Enter page B. the current user does not like it

Business:

  1. The user likes the operation, the number of likes on page B + 1, and the like status lights up
  2. Return to page A. the number of likes for this topic is + 1, and the like status is on

LiveData solution:

Create a LiveData of topic data

  1. Add an observer of topic data to page A
  2. After the business operation of page B is completed, use LiveData to send the changed topic data
  3. Page B returns to page A, which becomes active, and then receives the topic data sent before page B
  4. Page A replaces the old topic data and updates the list UI

You might have thought before you had LiveData

  1. EventBus: external libraries need to be added. Lifecycle registration and deregistration need to be handled
  2. Use the api to register the broadcast, but not to register the broadcast
  3. java observer api: it is troublesome to implement some classes. It also needs to handle the registration and deregistration of life cycle observers

By comparing the methods of LiveData, we can see that the LiveData scheme is relatively simple and clear

  1. Maintenance free life cycle
  2. Register the observer and notify the observer
  3. Focus on business processing

Scenario 2 app Download

There will be no details here

Scene 3 Music Player

The current owner is audio distribution, and I am mainly responsible for the watch end business

The audio playback UI update business is going to be reconstructed with LiveData (currently using EventBus)

Problem | solution

Switching topics leads to page reconstruction problems

Scenario: click the item in the news list, and a dialog will pop up to show the details. Then close the dialog, the ide will simulate killing the application, switch back to the application from the application taskbar, and the dialog page will be displayed

There are two classes 1 NewListActivity,2.NewViewModel

NewListActivity

public class NewListActivity ...{
  private NewViewModel vm;
  public void initData(){
    vm=new ViewModelProvider(this).get(NewViewModel.java)
		vm.liveData.observe(this,new Observer<Object>() {
         public void onChanged(Object obj){
             //showDialog
         }
    });
  }
}

NewViewModel

public class NewViewModel..{
  	public LiveData liveData..
    public loadNewDetail(){
      //Request news details
      //liveData.setValue() notification update UI
    }
}

Reason: the page is destroyed first and then rebuilt, so the Observer will be removed and the rebuilt Observer will be added again. It can be seen from the source code that the newly registered Observer will receive the notification of LiveData.

next

The second part of LiveData source code analysis intends to analyze the extension of LiveData

  1. Transformations map switchMap source code analysis
  2. Source code analysis of subclass MediatorLiveData