Explain the working principle of Android JetPack and LiveData in detail

  • 2021-12-11 19:13:20
  • OfStack

Preface to the table of contents Introduction Principle analysis

Preface

This article mainly explains the working principle of LiveData. If you don't know how to use LiveData, please refer to the official documents. The explanation of LiveData involves the knowledge of Lifecycle. If you don't know LifeCycle, please refer to the introduction of LifeCycle.

Introduction

LiveData is a data-holding class that can be observed by other components by adding observers. Unlike ordinary observers, its most important feature is to comply with the life cycle of the application. For example, in Activity, if the data is updated but Activity is already in destroy state, LivaeData will not notify Activity (observer). Of course. LiveData has many advantages, such as no memory leakage.

LiveData is usually used in conjunction with ViewModel, and ViewModel is responsible for triggering data updates, which are notified to LiveData, and then LiveData notifies active observers.

Principle analysis

Let's look directly at the code:


public class UserProfileViewModel extends ViewModel {
 private String userId;
 private MutableLiveData<User> user;
 private UserRepository userRepo;

 public void init(String userId) {
  this.userId = userId;
  userRepo = new UserRepository();
  user = userRepo.getUser(userId);
 }

 public void refresh(String userId) {
  user = userRepo.getUser(userId);
 }

 public MutableLiveData<User> getUser() {
  return user;
 }

}

The above UserProfileViewModel internally holds the reference of MutableLiveData in UserRepository, and provides the method of obtaining MutableLiveData getUser (). UserRepository is responsible for obtaining data from network or database, encapsulating it into MutableLiveData and then providing it to ViewModel.

We registered observers for MutableLiveData in UserProfileFragment, as follows:


@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
 super.onActivityCreated(savedInstanceState);
 String userId = getArguments().getString(UID_KEY);
 viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
 viewModel.init(userId);
 // Labeling 1
 viewModel.getUser().observe(UserProfileFragment.this, new Observer<User>() {
  @Override
  public void onChanged(@Nullable User user) {
   if (user != null) {
    tvUser.setText(user.toString());
   }
  }
 });
}

Look at annotation 1, viewModel. getUser () gets MutableLiveData, which is our LiveData, then calls observer method of LiveData and passes UserProfileFragment as a parameter. The observer () method is the entry point for our analysis. Next, let's look at what observer () method of LiveData does:


@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
 // Labeling 1
 if (owner.getLifecycle().getCurrentState() == DESTROYED) {
  // ignore
  return;
 }
 // Labeling 2
 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
 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;
 }
 owner.getLifecycle().addObserver(wrapper);
}

As you can see, UserProfileFragment is passed in as an LifeCycleOwner parameter. If your support package version is greater than or equal to 26.1.0, Fragment in support package will inherit from LifecycleOwner by default, and LifecycleOwner can get LifeCycle of this component, thus knowing the life cycle of UserProfileFragment component (everyone already knows LifeCycle by default here).

Look at Mark 1. If our UserProfileFragment component is already in destroy state, it will return directly and will not be joined as an observer. If it is not destroy state, go to Mark 2, create a new LifecycleBoundObserver to save our LifecycleOwner and observer, and then call mObservers. putIfAbsent (observer, wrapper) to store observer and wrapper as key and value in Map respectively. putIfAbsent () method will judge if value can already exist, it will return, otherwise it will return null. If existing is returned as null, indicating that this observer has not been added before, LifecycleBoundObserver is regarded as an observer of owner life cycle, that is, as an observer of UserProfileFragment life cycle.

Let's look at the LifecycleBoundObserver source code:


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

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

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

 @Override
 public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
  if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
   removeObserver(mObserver);
   return;
  }
  activeStateChanged(shouldBeActive());
 }

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

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

There are not many codes. LifecycleBoundObserver inherits from ObserverWrapper and implements GenericLifecycleObserver interface, while GenericLifecycleObserver interface inherits from LifecycleObserver interface. According to the characteristics of Lifecycle, observers who implement LifecycleObserver interface and join LifecycleOwner can perceive or actively obtain the state of LifecycleOwner.

Ok, after watching the observer, when will our LiveData inform the observer? Don't think about it, it must be the time of data update, and the data update is controlled by our code itself. For example, after requesting the network to return User information, we will take the initiative to put User into MutableLiveData. Here I directly simulate the network request in UserRepository as follows:


public class UserRepository {
 final MutableLiveData<User> data = new MutableLiveData<>();

 public MutableLiveData<User> getUser(final String userId) {
  if ("xiasm".equals(userId)) {
   data.setValue(new User(userId, " Xia Shengming "));
  } else if ("123456".equals(userId)) {
   data.setValue(new User(userId, " Ha ha ha "));
  } else {
   data.setValue(new User(userId, "unknow"));
  }
  return data;
 }
}

When the getUser () method is called, we call the setValue () method of MutableLiveData to put the data into LiveData, where MutableLiveData is actually inherited from LiveData, nothing special:


public class MutableLiveData<T> extends LiveData<T> {
 @Override
 public void postValue(T value) {
  super.postValue(value);
 }

 @Override
 public void setValue(T value) {
  super.setValue(value);
 }
}

setValue () must be on the main thread when it is put into User, otherwise it will report an error, while postValue does not have this check, but will pass data to the main thread. Let's look directly at the setValue () method:


@MainThread
protected void setValue(T value) {
 assertMainThread("setValue");
 mVersion++;
 mData = value;
 dispatchingValue(null);
}

First, call assertMainThread () to check whether it is in the main thread, then assign the data to be updated to mData, and then call dispatchingValue () method and pass it into null to distribute the data to various observers, such as our UserProfileFragment. Look at the dispatchingValue () method implementation:


private void dispatchingValue(@Nullable ObserverWrapper initiator) {
 if (mDispatchingValue) {
  mDispatchInvalidated = true;
  return;
 }
 mDispatchingValue = true;
 do {
  mDispatchInvalidated = false;
  // Labeling 1
  if (initiator != null) {
   considerNotify(initiator);
   initiator = null;
  } else {
   // Labeling 2
   for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
     mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
    considerNotify(iterator.next().getValue());
    if (mDispatchInvalidated) {
     break;
    }
   }
  }
 } while (mDispatchInvalidated);
 mDispatchingValue = false;
}

As can be seen from annotation 1, the difference between passing null and not passing null in dispatchingValue () parameter is that if null is passed, all observers will be notified, whereas only the incoming observers will be notified. Let's look at annotation 2 directly, inform all observers to get all ObserverWrapper by traversing mObservers, which is actually LifecycleBoundObserver mentioned above, and inform observers to call considerNotify () method, which is the concrete implementation of notification.


private void considerNotify(ObserverWrapper observer) {
 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.
 if (!observer.shouldBeActive()) {
  observer.activeStateChanged(false);
  return;
 }
 if (observer.mLastVersion >= mVersion) {
  return;
 }
 observer.mLastVersion = mVersion;
 //noinspection unchecked
 observer.mObserver.onChanged((T) mData);
}

If the observer is not active, this observer will not be notified. Look at the last line, observer. mObserver. onChanged ((T) mData), observer. mObserver is the Observer passed in by calling the observer () method of LiveData, and then calling the onChanged ((T) mData) method of Observer to pass in the saved data mData, thus realizing the update. Let's take a look at the Observer we implemented:


viewModel.getUser().observe(UserProfileFragment.this, new Observer<User>() {
 @Override
 public void onChanged(@Nullable User user) {
  if (user != null) {
   tvUser.setText(user.toString());
  }
 }
});

If any control is to be updated in response to changes to user, it can be handled in the onChanged () method. At this point, LiveData has been analyzed, but the implementation of LiveData still depends on Lifecycle.

The above is the detailed explanation of the working principle of Android JetPack LiveData. For more information about the working principle of Android JetPack LiveData, please pay attention to other related articles on this site!


Related articles: