[[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 - public class NameViewModel extends ViewModel {
- // Create a LiveData with a String
- private MutableLiveData<String> mCurrentName;
- public MutableLiveData<String> getCurrentName() {
- if (mCurrentName == null ) {
- mCurrentName = new MutableLiveData<String>();
- }
- return mCurrentName;
- }
- // Rest of the ViewModel...
- }
Implement monitoring in activity or fragment to update UI - public class NameActivity extends AppCompatActivity {
- private NameViewModel mModel;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Other code to setup the activity...
- // Get the ViewModel.
- mModel = ViewModelProviders. of (this).get(NameViewModel.class);
- // Create the observer which updates the UI.
- final Observer<String> nameObserver = new Observer<String>() {
- @Override
- public void onChanged(@Nullable final String newName) {
- // Update the UI, in this case , a TextView.
- mNameTextView.setText(newName);
- //Note that this is not written like this in MVVM. This is just a single line binding
- }
- };
- // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
- mModel.getCurrentName().observe(this, nameObserver);
- //Note that this place does not use Java8 lambda expressions, which can be written more concisely.
- }
- }
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; - mButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick( View v) {
- String anotherName = "John Doe" ;
- mModel.getCurrentName().setValue(anotherName);
- }
- });
- mButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick( View v) {
- String anotherName = "John Doe" ;
- mModel.getCurrentName().setValue(anotherName);
- }
- });
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;
- // The key of the map is the LiveData data observer, and the value is the Lifecycle observer of the component
- private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
- new SafeIterableMap<>();
- @MainThread
- public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
- assertMainThread( "observe" );
- //Judge the current life cycle status
- if (owner.getLifecycle().getCurrentState() == DESTROYED) {
- // ignore
- return ;
- }
- //Lifecycle life cycle change logic is here
- LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
- //mObservers save
- 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 the lifecycle life cycle changes
- owner.getLifecycle().addObserver(wrapper);
- }
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;
- class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
- @NonNull
- //General components, such as Activity and fragment, can implement LifecycleOwner and get 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() {
- return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
- }
- @Override
- public void onStateChanged(@NonNull LifecycleOwner source,
- @NonNull Lifecycle.Event event) {
- //Get the current life cycle status
- 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();
- }
- }
- /* Determine whether to bind */
- @Override
- boolean isAttachedTo(LifecycleOwner owner) {
- return mOwner == owner;
- }
- /* Remove observation */
- @Override
- void detachObserver() {
- mOwner.getLifecycle().removeObserver(this);
- }
- }
3.activeStateChanged There is a method in the ObserverWrapper class, which will be used in the sticky events later; - void activeStateChanged(boolean newActive) {
- //If the component status does not change, return
- 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);
- }
- }
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; - 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);
- }
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; - private final Runnable mPostValueRunnable = new Runnable() {
- @SuppressWarnings( "unchecked" )
- @Override
- public void run() {
- Object newValue;
- synchronized (mDataLock) {
- newValue = mPendingData;
- mPendingData = NOT_SET;
- }
- setValue((T) newValue);
- }
- };
4.3. postToMainThread main thread Handler The postToMainThread method of the ArchTaskExecutor class is actually the postToMainThread method executed by the DefaultTaskExecutor class; - public class ArchTaskExecutor extends TaskExecutor {
- @NonNull
- private TaskExecutor mDelegate;
- @NonNull
- private TaskExecutor mDefaultTaskExecutor;
- private ArchTaskExecutor() {
- mDefaultTaskExecutor = new DefaultTaskExecutor();
- mDelegate = mDefaultTaskExecutor;
- }
- ...
- @Override
- public void postToMainThread(Runnable runnable) {
- mDelegate.postToMainThread(runnable);
- }
- ...
- }
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; - 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 ConstantConditions
- mMainHandler.post(runnable);
- }
- }
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; - @MainThread
- protected void setValue(T value) {
- assertMainThread( "setValue" );
- mVersion++;
- mData = value;
- dispatchingValue( null );
- }
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; - @SuppressWarnings( "WeakerAccess" ) /* synthetic access */
- void dispatchingValue(@Nullable ObserverWrapper initiator) {
- if (mDispatchingValue) {
- mDispatchInvalidated = true ;
- return ;
- }
- mDispatchingValue = true ;
- do {
- mDispatchInvalidated = false ;
- if (initiator != null ) {
- //Sticky events, a single observer receives data
- considerNotify(initiator);
- initiator = null ;
- } else {
- //setvalue passed in initiator is empty, execute here
- // Take out 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 ;
- }
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
- private void considerNotify(ObserverWrapper observer) {
- //Is the component 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 .
- //Judge sticky events
- if (!observer.shouldBeActive()) {
- observer.activeStateChanged( false );
- return ;
- }
- // Check if data has been sent
- if (observer.mLastVersion >= mVersion) {
- return ;
- }
- observer.mLastVersion = mVersion;
- //Send data
- observer.mObserver.onChanged((T) mData);
- }
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; |