Personal blog

www.milovetingting.cn

Jetpack – LiveData study

What is LiveData

LiveData is an observable data store class with lifecycle awareness.

Simple to use

LiveData is typically used with viewModels. Define a class from ViewModel:

public class LiveDataSub extends ViewModel {

    private MutableLiveData<String> infos;

    private int number;

    public MutableLiveData<String> getInfo(a) {
        if (infos == null) {
            infos = new MutableLiveData<>();
        }
        return infos;
    }

    public int increaseNumber(a) {
        number++;
        returnnumber; }}Copy the code

This class defines attributes of type MutableLiveData and provides the external access method getInfo

Used in an Activity

public class LiveDataActivity extends AppCompatActivity {

    private TextView tv;

    private LiveDataSub viewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_livedata);
        tv = findViewById(R.id.tv);
        viewModel = ViewModelProviders.of(this).get(LiveDataSub.class);
        viewModel.getInfo().observe(this.new Observer<String>() {
            @Override
            public void onChanged(String s) { tv.setText(s); }}); }public void update(View view) {
        String info = "info:"+ viewModel.increaseNumber(); viewModel.getInfo().setValue(info); }}Copy the code

Instantiate the ViewModel defined by viewModelproviders.of (this).get(LiveDataSub. Class) and add observations of the current Activity by calling LiveData’s Observe method.

LiveData setValue can be used to update data, and the interface will be automatically updated.

The principle of

Look at LiveData’s Observe method

Add the observer

@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

The observe method is called on the main thread. Lifecycle is returned without adding an Observer if the Activity corresponding to lifecycle is already in a DESTROYED state. Lifecycle’s addObserver method was eventually called.

Data update

Data updates are performed through the setValue method of LiveData.

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

This method requires execution on the main thread. The dispatchingValue method is called internally

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

Since the ObserverWrapper passed in earlier is null, the notice method in the following iteration is executed

 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

In this method, the observer onChange method is finally called.

Sequence diagram:

Asynchronous data update

The above data update is performed on the UI thread, and if you want to perform it on a child thread, you need to use the postValue method. Let’s look at this method as well

protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            // Check whether mPendingData has been assigned a value
            postTask = mPendingData == NOT_SET;
            // Assign to mPendingData
            mPendingData = value;
        }
        // Cancel sending if the value is already assigned
        if(! postTask) {return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
Copy the code

This method is executed through the postToMainThread method of ArchTaskExecutor.

See first ArchTaskExecutor. GetInstance () method

public static ArchTaskExecutor getInstance(a) {
        if(sInstance ! =null) {
            return sInstance;
        }
        synchronized (ArchTaskExecutor.class) {
            if (sInstance == null) {
                sInstance = newArchTaskExecutor(); }}return sInstance;
    }
Copy the code

Now let’s look at the constructor

private ArchTaskExecutor(a) {
        mDefaultTaskExecutor = new DefaultTaskExecutor();
        mDelegate = mDefaultTaskExecutor;
    }
Copy the code

Here mDefaultTaskExecutor and mDelegate are instantiated

Call the postToMainThread method

@Override
    public void postToMainThread(Runnable runnable) {
        mDelegate.postToMainThread(runnable);
    }
Copy the code

The postToMainThread method of DefaultTaskExecutor is executed

public void postToMainThread(Runnable runnable) {
        if (mMainHandler == null) {
            synchronized (mLock) {
                if (mMainHandler == null) {
                    mMainHandler = newHandler(Looper.getMainLooper()); }}}//noinspection ConstantConditions
        mMainHandler.post(runnable);
    }
Copy the code

As you can see, it’s just Posting a runnable to the main thread.

private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run(a) {
            Object newValue;
            synchronized (mDataLock) {
                // Get the data to update
                newValue = mPendingData;
                / / mPendingData reset
                mPendingData = NOT_SET;
            }
            //noinspection uncheckedsetValue((T) newValue); }};Copy the code

The final callback to the run method is the setValue method that was called. In the run method, the data to be updated is retrieved and then reset to mPendingData. Therefore, if postValue is called multiple times, if the previous update has not been processed, no update message is sent to the main thread, only mPendingData is assigned, and in the run callback, the last data is retrieved.