Introduction to the

LiveData is a component in Jetpack. It is a data storage class that can be observed and has the ability to sense the component life cycle. LiveData can sense the active state of the component life cycle and send data updates. Most are used in conjunction with components that have a lifecycle (such as activities, fragments, or services, or objects that implement the LifecycleOwner interface).

role

So what does LiveData do? It mainly has the following two functions:

  • Refreshing Data in Real time
  • Preventing memory leaks

LiveData adopts the observer mode. When the data saved by LiveData changes, it will notify the observer. After receiving the notification, the observer can refresh UI data or perform other operations.

So how does it prevent memory leaks? When you add an observer to LiveData, you can bind it to a component that has a life cycle. The observer will be notified of updates when the component is in an active state (STARTED, RESUMED), and will be automatically removed when the component is destroyed. This prevents memory leaks from holding the corresponding component all the time.

Hello LiveData

It’s customary to start any new technology with a Hello World, so let’s take a look at Hello LiveData.

Add the dependent

Add LiveData to module gradle. Gradle as follows:

dependencies {
	def lifecycle_version = 1.1.1 ""
    implementation 'com. Android. Support: appcompat - v7:28.0.0'
	implementation "android.arch.lifecycle:livedata:$lifecycle_version"
}
Copy the code

If you use Androidx, you need to import the corresponding LiveData package and AppCompat package under Androidx:

dependencies {
	def lifecycle_version = 1.1.1 ""
    implementation 'androidx. Appcompat: appcompat: 1.0.0 - beta01'
	implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
}
Copy the code

use

Now that dependency packages have been introduced, how can I quickly use LiveData

Create a MutableLiveData object, which is a subclass of LiveData, and add an observer object to it as follows:

java:

final MutableLiveData<String> simpleLiveData = new MutableLiveData<>();

Observer<String> observer = new Observer<String>() {
    @Override
    public void onChanged(@Nullable String text) { mTextView.setText(text); }}; simpleLiveData.observe(this, observer);
Copy the code

kotlin:

val simpleLiveData = MutableLiveData<String>()
val observer = Observer<String> { text ->
    mTextView.text  = text
}
simpleLiveData.observe(this, observer)
Copy the code

This in the Observe method is an object that implements the LifecycleOwner interface, such as AppCompatActivity in support

STARTED or RESUMED when we update simpleLiveData data and the observer bound lifecycle component (such as an Activity/Fragment object that implements the LifecycleOwner interface) is active The Observer callback is triggered to update the value of the mTextView, which is UI data update.

For example, clicking the button to change the value of simpleLiveData to “Hello LiveData” triggers the onChanged method of the Observer

mButton.setOnClickListener(view -> {
     simpleLiveData.setValue("Hello LiveData")});Copy the code

The summary is as follows:

  • Create a LiveData object:new MutableLiveData<>()
  • Create an observer object:new Observer()
  • Bind the observer object:LiveData.observe
  • Update LiveData data: LiveData.setValue

Detailed introduction

1. Api introduction

LiveData is an abstract class with generics and has two subclassesMutableLiveDataMediatorLiveDataLet’s look at the diagram of the LiveData class:

  • public T getValue(): Get the data in LiveData
  • public boolean hasActiveObservers(): Whether there are active observer objects
  • public boolean hasObservers(): Indicates whether there are observer objects
  • public void observe(LifecycleOwner owner, Observer<? super T> observer) : Adds an observer object aware of the lifecycle
  • public void observeForever(Observer<? super T> observer): Adds a non-lifecycle aware observer object
  • public void removeObserver(final Observer<? super T> observer): Removes the corresponding observer object
  • public void removeObservers(final LifecycleOwner owner): Removes the observer object based on the lifecycle object
  • protected void setValue(T value): Sets the data of the LiveData container
  • protected void postValue(T value): Sets the data of the LiveData container on the main thread
  • protected void onActive(): called when the number of active observer objects is greater than 0, i.e. when there are active observer objects
  • protected void onInactive(): called when the number of active observer objects is equal to 0, that is, when there are no active observer objects

MutableLiveData: MutableLiveData, the most commonly used subclass of LiveData. Its implementation is very simple, is to inherit LiveData and expose setValue, postValue methods

MediatorLiveData: Inherited from MutableLiveData, MediatorLiveData can listen to multiple LiveData sources, or schedule multiple LiveData sources to determine which LiveData updates to send to the observer. It adds two new methods, addSource and removeSource, to add and remove LiveData sources

Observer: Indicates an Observer interface for observing LiveData

2, detailed use

The use of MutableLiveData

Hello LiveData briefly demonstrated the use of LiveData earlier

In addition to relying on lifecycle objects for automatic observer management, LiveData can also add lifecycle ignoring observers, using the observeForever method:

java:

MutableLiveData<String> liveData = new MutableLiveData<>();
liveData.observeForever(new Observer<String>() {
    @Override
    public void onChanged(String s) {
        //do something}});Copy the code

kotlin:

val liveData = MutableLiveData<String>()
liveData.observeForever {
    //do something
}
Copy the code

In this case, you need to manually call removeObserver to remove the observer when no observation is needed to prevent memory leaks.

Transformation operation zoom

You can perform Transformations on LiveData in a quick way, or you can perform Transformations on LiveData in a quick way. It provides two operators map and switchMap, both of which convert a LiveData object to another LiveData object. When the value of one LiveData changes, the value of the other LiveData also changes. To see how this works, use map to convert LiveData

to LiveData

:

java:

final MutableLiveData<User> userLiveData = new MutableLiveData<>();
final LiveData<String> userDescribe = Transformations.map(userLiveData, new Function<User, String>() {
     @Override
     public String apply(User user) {
         return "id:" + user.getId() + ", name:" + user.getName() + ", age:"+ user.getAge(); }});Copy the code

kotlin:

val userLiveData = MutableLiveData<User>()
val userDescribe = Transformations.map(userLiveData) { user -> 
	"id: ${user.id}  name: ${user.name} age: ${user.age}" 
}
Copy the code

When the value of userLiveData changes, the value of userDescribe changes as well.

Convert LiveData

to LiveData

using switchMap:

java:

private LiveData<User> getUser(long id){
	/ /...
}
//....
final MutableLiveData<Long> userIdLiveData = new MutableLiveData<>();
final LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, new Function<Long, LiveData<User>>() {
    @Override
    public LiveData<User> apply(Long id) {
        returngetUser(id); }});Copy the code

kotlin:

private fun getUser(id: Long): LiveData<User> {
	/ /...
}
/ /...
val userIdLiveData = MutableLiveData<Long> ()val userLiveData = Transformations.switchMap(userIdLiveData) { id -> 
	getUser(id)
}
Copy the code

MediatorLiveData

MediatorLiveData inherits from MutableLiveData and can add multiple LiveData sources and observe or schedule multiple LiveData sources. You can perform Transformations like MediatorLiveData or blah, blah, blah, blah, blah.

java:

MutableLiveData<User> userLiveData1 = new MutableLiveData<>();
MutableLiveData<User> userLiveData2 = new MutableLiveData<>();
MediatorLiveData<User> userMediatorLiveData = new MediatorLiveData<>();
userMediatorLiveData.addSource(userLiveData1, new Observer<User>() {
    @Override
    public void onChanged(User user) { userMediatorLiveData.setValue(user); }}); userMediatorLiveData.addSource(userLiveData2,new Observer<User>() {
    @Override
    public void onChanged(User user) { userMediatorLiveData.setValue(user); }});Copy the code

kotlin:

val userLiveData1 = MutableLiveData<User>()
val userLiveData2 = MutableLiveData<User>()
valuserMediatorLiveData = MediatorLiveData<User>() userMediatorLiveData.addSource(userLiveData1) { user -> userMediatorLiveData.value = user } userMediatorLiveData.addSource(userLiveData2) { user -> userMediatorLiveData.value =  user }Copy the code

UserMediatorLiveData = userLiveData1; userLiveData2 = userMediatorLiveData; UserMediatorLiveData is updated when any of the data is updated and during the active life of userMediatorLiveData. Some of you might wonder, what’s the use of MediatorLiveData? Set the listener to userLiveData1 and userLiveData1, and then set the changed data to the other liveData1.

val userLiveData1 = MutableLiveData<User>()
val userLiveData2 = MutableLiveData<User>()
val userLiveData = MutableLiveData<User>()
userLiveData1.observe(this, Observer { user -> 
    userLiveData.value = user
})  
userLiveData2.observe(this, Observer { user -> 
    userLiveData.value = user
})
Copy the code

UserLiveData1 and userLiveData2 set LifecycleOwner respectively. MediatorLiveData manages the life cycle of all LiveData added to it. MediatorLiveData overwrites the onActive and onInactive methods of LiveData to add and remove its internal LiveData observers uniformly

Custom LiveData

In addition to using MutableLiveData and MediatorLiveData provided by the library, we can also inherit LiveData and customize our own LiveData according to actual scenarios, such as MessageLiveData that we need to display the latest message, Let’s see how this works:

java:

public class MessageLiveData extends LiveData<String> {
    private MessageManager messageManager;
    public MessageLiveData(a){
        messageManager = MessageManager.getInstance();
    }

	// The latest message callback
    private MessageManager.MessageCallback messageCallback = new MessageManager.MessageCallback() {
        @Override
        public void onMessage(String message) { setValue(message); }};@Override
    protected void onActive(a) {
        super.onActive();
        messageManager.addMessageCallback(messageCallback);
    }

    @Override
    protected void onInactive(a) {
        super.onInactive(); messageManager.removeMessageCallback(messageCallback); }}Copy the code

kotlin:

class MessageLiveData : LiveData<String>() {

    private val messageManager:MessageManager by lazy{
        MessageManager.getInstance()
    }

    private val messageCallback = MessageManager.MessageCallback { message ->
        value = message
    }

    override fun onActive(a) {
        super.onActive()
        messageManager.addMessageCallback(messageCallback)
    }

    override fun onInactive(a) {
        super.onInactive()
        messageManager.removeMessageCallback(messageCallback)
    }
}
Copy the code

MessageLiveData inherited from LiveData registers message listening in onActive and removes listener in onInactive, so that we can use MessageLiveData to observe the latest messages.

LiveData is used in conjunction with ViewModel

LiveData is used directly in activities, but in real development scenarios, we usually use it in ViewModel rather than directly in activities/fragments. Then observe the changes to the LiveData data in the ViewModel in the Activity/Fragment:

java:

public class MainViewModel extends ViewModel {
    public MutableLiveData<User> userLiveData = new MutableLiveData<>();

    public void loadUser(a){
        / /...userLiveData.setValue(user); }}//Activity
MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
viewModel.userLiveData.observe(this.new Observer<User>() {
    @Override
    public void onChanged(User user) { mTextView.setText(user.getName()); }});Copy the code

kotlin:

class MainViewModel : ViewModel() {
    var userLiveData = MutableLiveData<User>()

    fun loadUser(a) {
        / /...

        userLiveData.setValue(user)
    }
}
//Activity
val viewModel = ViewModelProviders.of(this)[MainViewModel::class.java]
viewModel.userLiveData.observe(this, Observer { user ->
    mTextView.text = user.name
})
Copy the code

See Jetpack’s ViewModel for more information about viewModels

LiveData is used in conjunction with DataBinding

Now look at the use of LiveData in conjunction with DataBinding, or MainViewModel as used above:

java:

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setLifecycleOwner(this);
MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
binding.setVm(viewModel);
Copy the code

kotlin:

val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.setLifecycleOwner(this)
val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
binding.vm = viewModel
Copy the code

Activity_main:


      
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="vm"
            type="com.example.livedata.MainViewModel"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{vm.userLiveData.name}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Copy the code

Instead of using DataBinding’s Observable in the ViewModel, we use LiveData, At the time of data binding to ViewDataBinding set LifecycleOwner namely binding. SetLifecycleOwner (this), When data is bound, ViewDataBinding internally automatically adds an observer object to the bound LiveData object to observe updates to the data and refresh the UI data.

For more information about DataBinding, see Jetpack’s DataBinding

The principle of

Having introduced the use of LiveData, let’s take a look at how LiveData internally implements the observation method that calls back to the observer only when the lifecycle is active.

Observe the observe method:

MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    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);
}
Copy the code

Check whether they are in the main thread first and then check the life cycle state. LifecycleOwner and Observer are then wrapped as LifecycleBoundObserver into the mObservers Map and added to the lifecycle observation. LifecycleBoundObserver inherits ObserverWrapper and implements the GenericLifecycleObserver interface, listening for lifecycle changes inside onStateChanged:

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

The observer is removed in the lifecycle state and activeStateChanged is called in the other states to handle whether the observer is called back. This automatically manages the observer based on the lifecycle.

Then look at the setValue method:

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

Continue with the dispatchingValue method:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if(initiator ! =null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            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

Emphasis on notice method:

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);
}
Copy the code

Check ObserverWrapper, the LifecycleBoundObserver wrapped in the previous observe method, is active and call shouldBeActive. LifecycleBoundObserver is all about determining whether the life cycle is active or not

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

Then the last version of ObserverWrapper is compared to the current version, if >= return, the current version of the setValue method is called mVersion++ each time, and finally the observer callback is called, The onChanged method of the Observer that we passed in.

Now look at the observeForever method:

@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
    assertMainThread("observeForever");
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if(existing ! =null && existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if(existing ! =null) {
        return;
    }
    wrapper.activeStateChanged(true);
}
Copy the code

It encapsulates the Observer as AlwaysActiveObserver, and its shouldBeActive method returns true directly and calls activeStateChanged(true); Set active to true, that is, always active, so you can always watch data updates.

For a full description of Lifecycle see Jetpack’s Lifecycle

The original link