Welcome to follow my public account, wechat search Hu Feiyang, article updates can be the first time to receive.

Jetpack AAC Series:

(1) Lifecycle will be fully mastered!

“Finally get it” series: Jetpack AAC complete analysis (2) Fully master LiveData!

“Finally get it” series: Jetpack AAC complete analysis (3) fully master the ViewModel!

“Finally Get it” series: Jetpack AAC Complete Analysis (iv) MVVM – Android Architecture Exploration!

“Finally Get it” series: Jetpack AAC Complete Analysis (5) DataBinding re-cognition!

The last article introduced Lifecycle, the underlying component of Jetpack AAC, which is used to manage the Lifecycle of an Activity/Fragment. This article introduces LiveData, a Lifecycle based component used to process data.

First, LiveData introduction

1.1 role

LiveData is an important component of Jetpack AAC and also has an abstract class of the same name.

LiveData, originally meaning LiveData. Can data still be alive? Let’s start with the official definition:

LiveData is an observable data storage class. Unlike regular observable classes, LiveData is lifecycle aware, meaning that it follows the life cycle of other application components, such as activities/fragments. This awareness ensures that LiveData updates only application component observers that are in an active lifecycle state.

Break down:

  1. LiveData is a data holder that wraps a layer around the source data.
  2. When the source data is wrapped in LiveData, it can be observed by the Observer and can be sensed when the data is updated.
  3. However, the awareness of an observer can only occur when an Activity or Fragment is in the active lifecycle state (STARTED or RESUMED).

That is, LiveData makes updates to the data aware by the observer in observer mode, and this awareness only occurs in the LifecycleOwner’s active life cycle state.

1.2 the characteristics of

Using LiveData has the following advantages:

  • Make sure the interface conforms to the data state, and When the lifecycle state changes, LiveData informs the Observer that the interface can be updated in the Observer. The observer can refresh the interface each time the lifecycle state changes, rather than each time the data changes.
  • The memory leak will not occur and the observer will be automatically removed after the LifecycleOwner state becomes DESTROYED.
  • There is no crash due to Activity stopping, and if the LifecycleOwner lifecycle is inactive, it does not receive any LiveData events.
  • There is no need to unobserve LiveData manually. Developers do not need to unobserve LiveData in onPause or onDestroy methods, because LiveData is aware of lifecycle state changes and automatically manages all these operations.
  • The data is always up to date, and if LifecycleOwner is inactive when the data is updated, it receives the latest data when it becomes active. For example, an Activity that was in the background will receive the latest data as soon as it returns to the foreground.

Second, the use of LiveData

The following is an introduction to the use of LiveData, and you can better understand the above content by mastering how to use it.

2.1 Basic Usage

Gradle dependencies were introduced in the previous article. Here’s the basic usage:

  1. Create a LiveData instance, specifying the source data type
  2. Create an Observer instance that implements the onChanged() method to receive source data changes and refresh the UI
  3. The LiveData instance adds an observer using the observe() method and passes LifecycleOwner
  4. The LiveData instance updates the source data with setValue()/postValue() (the child thread wants postValue()))

Here’s an example:

public class LiveDataTestActivity extends AppCompatActivity{

   private MutableLiveData<String> mLiveData;
   
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_lifecycle_test);
       
       // Basic use of liveData
       mLiveData = new MutableLiveData<>();
       mLiveData.observe(this.new Observer<String>() {
           @Override
           public void onChanged(String s) {
               Log.i(TAG, "onChanged: "+s); }}); Log.i(TAG,"onCreate: ");
       mLiveData.setValue("onCreate");// The activity is inactive and does not call onChanged. When it becomes active, value is overwritten by value in onStart
   }
   @Override
   protected void onStart(a) {
       super.onStart();
       Log.i(TAG, "onStart: ");
       mLiveData.setValue("onStart");// Active status, the callback onChanged. And value overrides the value set in onCreate and onStop
   }
   @Override
   protected void onResume(a) {
       super.onResume();
       Log.i(TAG, "onResume: ");
       mLiveData.setValue("onResume");// Active state, callback onChanged
   }
   @Override
   protected void onPause(a) {
       super.onPause();
       Log.i(TAG, "onPause: ");
       mLiveData.setValue("onPause");// Active state, callback onChanged
   }
   @Override
   protected void onStop(a) {
       super.onStop();
       Log.i(TAG, "onStop: ");
       mLiveData.setValue("onStop");// Inactive, no callback onChanged. When it becomes active later, value is overwritten by value in onStart
   }
   @Override
   protected void onDestroy(a) {
       super.onDestroy();
       Log.i(TAG, "onDestroy: ");
       mLiveData.setValue("onDestroy");// The Observer has been removed. There is no onChanged callback}}Copy the code

Note that the LiveData instance mLiveData was created using MutableLiveData, which is the LiveData implementation class and specifies the type of the source data as String. An instance of the interface Observer is then created, implementing its onChanged() method to receive changes to the source data. The observer calls mLiveData’s observe() method with the Activity as an argument, indicating that the observer starts observing mLiveData. The setValue() method of mLiveData is then called in all of the Activity’s lifecycle methods. The result log is as follows:

// Open the page,
2020-11-22 20:23:29.865 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onCreate: 
2020-11-22 20:23:29.867 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStart: 
2020-11-22 20:23:29.868 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onStart
2020-11-22 20:23:29.869 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onResume: 
2020-11-22 20:23:29.869 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onResume
/ / in accordance with the Home button
2020-11-22 20:23:34.349 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onPause: 
2020-11-22 20:23:34.349 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onPause
2020-11-22 20:23:34.368 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStop: 
/ / points to open again
2020-11-22 20:23:39.145 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onStart: 
2020-11-22 20:23:39.146 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onStart
2020-11-22 20:23:39.147 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onResume: 
2020-11-22 20:23:39.147 13360-13360/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onResume
// Return key to exit
2020-11-22 20:23:56.753 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onPause: 
2020-11-22 21:23:56.753 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onChanged: onPause
2020-11-22 20:23:58.320 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onStop: 
2020-11-22 20:23:58.322 14432-14432/com.hfy.androidlearning I/Lifecycle_Test: onDestroy: 
Copy the code
  • First open the page, and setValue in onCreate(). Since the activity is inactive, there is no immediate callback to onChanged. When onStart() becomes active, onChanged is called, but value is overwritten by the value of setValue in onStart(), so onChanged: onStart is printed. (Why not print twice in a row? Because the ON_START event does not become active until onStart() returns, that is, after onStart() has finished (see previous article), and the observer receives the latest data. Then go to onResume(), which also sets value, which is also active, so immediately call back onChanged and print onChanged: onResume

  • When the Home key is pressed, the onPause() setValue, active state, immediately calls back to the onChanged method. SetValue does not immediately call back to the onChanged method when onStop() has become inactive.

  • When onStart() becomes active, onChanged is called, but value is overwritten by the value of setValue in onStart(), so onChanged: onStart is printed. Then go to onResume(), which also sets value, which is also active, so immediately call back onChanged.

  • When the return key exits, onPause()/onStop() has the same effect as pressing the Home key. SetValue in onDestroy() is inactive and the observer has been removed. There is no callback to onChanged.

Alternatively, in addition to using the observe() method to add observers, you can also use the observeForever(Observer) method to register observers that are not associated with LifecycleOwner. In this case, the observer is considered to be always active.

2.2 Extended Usage

Extensions include two things:

  1. Custom LiveData, overridden by its own callback methods: onActive(), onInactive().
  2. Implement LiveData as a singleton mode, easy to share data between multiple activities, fragments.

Here’s the official example:

public class StockLiveData extends LiveData<BigDecimal> {
        private static StockLiveData sInstance; / / a single instance
        private StockManager stockManager;

        private SimplePriceListener listener = new SimplePriceListener() {
            @Override
            public void onPriceChanged(BigDecimal price) {
                setValue(price);SetValue (price) = setValue(price) = setValue(price}};// Get the singleton
        @MainThread
        public static StockLiveData get(String symbol) {
            if (sInstance == null) {
                sInstance = new StockLiveData(symbol);
            }
            return sInstance;
        }

        private StockLiveData(String symbol) {
            stockManager = new StockManager(symbol);
        }

     	Called when the number of active observers (LifecycleOwner) changes from 0 to 1
        @Override
        protected void onActive(a) {
            stockManager.requestPriceUpdates(listener);// Start to watch for stock price updates
        }

     	// Called when the number of active observers (LifecycleOwner) changes from 1 to 0. That doesn't mean there are no observers, it could mean they're all inactive. You can use hasObservers() to check whether there are observers.
        @Override
        protected void onInactive(a) {
            stockManager.removeUpdates(listener);// Remove the stock price update observation}}Copy the code

In order to observe the stock price changes, the custom StockLiveData is inherited from LiveData, and it is a singleton pattern, which can only be obtained through the get(String symbol) method. I rewrote onActive(), onInactive(), and added logic to start observing stock price updates and remove stock price update observations.

  • OnActive () is invoked when the number of active lifecycleOwners changes from 0 to 1.
  • OnInactive () is invoked when the number of active observers (lifecycleOwners) changes from 1 to 0.

That is, only when there is an active observer (LifecycleOwner) is connected to the price update service to listen for price changes. Use the following:

    public class MyFragment extends Fragment {
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            // Get a single instance of StockLiveData, add observers, and update the UI
            StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {
                // Update the UI.}); }}Copy the code

Since StockLiveData is in single-instance mode, data can be shared between multiple LifyCycleowners (Activity, Fragment).

2.3 Advanced Usage

If you want to make changes to the values stored in the LiveData object before dispatching it to an observer, or if you need to return a different LiveData instance based on the value of another instance, you can use the BEHAVES class provided in LiveData.

You can modify data very quickly – doubling. Map

        // The Integer type is liveData1
        MutableLiveData<Integer> liveData1 = new MutableLiveData<>();
        // Convert to a String liveDataMap
        LiveData<String> liveDataMap = Transformations.map(liveData1, new Function<Integer, String>() {
            @Override
            public String apply(Integer input) {
                String s = input + " + Transformations.map";
                Log.i(TAG, "apply: " + s);
                returns; }}); liveDataMap.observe(this.new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "onChanged1: "+s); }}); liveData1.setValue(100);
Copy the code

It’s easy to use: You can make changes in the liveData1 data that produce a new liveDataMap, add observers to the liveDataMap, and set the liveData1 data at the end.

This example changes liveData1 of type Integer to liveDataMap of type String. Here are the results:

2020-12-06 17:01:56.095 21998-21998/com.hfy.androidlearning I/Lifecycle_Test: apply: 100 + Transformations.map
2020-12-06 17:01:56.095 21998-21998/com.hfy.androidlearning I/Lifecycle_Test: onChanged1: 100 + Transformations.map
Copy the code

2.3.2 data switch – Transformations. SwitchMap

If you want to switch according to a certain value to observe different LiveData data, you can use Transformations. SwitchMap () method.

	// Two liveData. LiveDataSwitch decides which livaData data to return
        MutableLiveData<String> liveData3 = new MutableLiveData<>();
        MutableLiveData<String> liveData4 = new MutableLiveData<>();
        
	// The switching condition is LiveData. The value of liveDataSwitch is the switching condition
        MutableLiveData<Boolean> liveDataSwitch = new MutableLiveData<>();
        
	//liveDataSwitchMap is generated by the switchMap() method and is used to add observers
        LiveData<String> liveDataSwitchMap = Transformations.switchMap(liveDataSwitch, new Function<Boolean, LiveData<String>>() {
            @Override
            public LiveData<String> apply(Boolean input) {
            // Here is the specific switching logic: which liveData is returned according to the value of liveDataSwitch
                if (input) {
                    return liveData3;
                }
                returnliveData4; }}); liveDataSwitchMap.observe(this.new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "onChanged2: "+ s); }});boolean switchValue = true;
        liveDataSwitch.setValue(switchValue);// Set the switching condition value

        liveData3.setValue("liveData3");
        liveData4.setValue("liveData4");
Copy the code

LiveData3 and liveData4 are two data sources. There is a judgment condition to determine which data to fetch. The condition is liveDataSwitch. Transformations. SwitchMap () is used to implement this logic, the return value added liveDataSwitchMap observer. Here are the results:

2020-12-06 17:33:53.844 27347-27347/com.hfy.androidlearning I/Lifecycle_Test: switchValue=true
2020-12-06 17:33:53.847 27347-27347/com.hfy.androidlearning I/Lifecycle_Test: onChanged2: liveData3

2020-12-06 17:34:37.600 27628-27628/com.hfy.androidlearning I/Lifecycle_Test: switchValue=false
2020-12-06 17:34:37.602 27628-27628/com.hfy.androidlearning I/Lifecycle_Test: onChanged2: liveData4
Copy the code

“(You can do exactly the same thing with LivaData as you can with Rxjava.)

2.3.3 Viewing Multiple Data – MediatorLiveData

MediatorLiveData is a subclass of LiveData that allows multiple LiveData sources to be merged. Whenever any of the original LiveData source objects change, the observer of the MediatorLiveData object is triggered.

        MediatorLiveData<String> mediatorLiveData = new MediatorLiveData<>();

        MutableLiveData<String> liveData5 = new MutableLiveData<>();
        MutableLiveData<String> liveData6 = new MutableLiveData<>();

	// Add the source LiveData
        mediatorLiveData.addSource(liveData5, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "onChanged3: "+ s); mediatorLiveData.setValue(s); }});// Add the source LiveData
        mediatorLiveData.addSource(liveData6, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "onChanged4: "+ s); mediatorLiveData.setValue(s); }});// Add observations
        mediatorLiveData.observe(this.new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "onChanged5: "+s);
                // liveData5, liveData6 update, can receive}}); liveData5.setValue("liveData5");
        //liveData6.setValue("liveData6");
Copy the code

For example, if the interface has a LiveData object that can be updated from a local database or network, you can add the following sources to the MediatorLiveData object:

  • LiveData5 associated with data stored in a local database
  • LiveData6 associated with data accessed from the network

An Activity can receive updates from both sources simply by observing the MediatorLiveData object. Here are the results:

2020-12-06 17:56:17.870 29226-29226/com.hfy.androidlearning I/Lifecycle_Test: onChanged3: liveData5
2020-12-06 17:56:17.870 29226-29226/com.hfy.androidlearning I/Lifecycle_Test: onChanged5: liveData5
Copy the code

(Translations are also used in conjunction with MediatorLiveData.)

The use of LiveData is finished, the next start source analysis.

Three, source code analysis

How does LiveData, which has several features mentioned earlier, feel life cycle state changes and do not have to manually unobserve them?

3.1 Adding An Observer

LiveData principle is the observer mode, let’s start with the livedata.observe () method:

    /** * Add observer. Events are distributed on the main thread. If LiveData already has data, it will be distributed directly to the Observer. * The observer accepts events only when the LifecycleOwner is active. If the state is changed from the LifecycleOwner, the observer is automatically removed. * When data is updated while inactive, the observer does not receive it. When it becomes active, the latest data is automatically received. * When LifecycleOwner is not in the DESTROYED state, LiveData holds strong references to both the observer and owner. When LifecycleOwner is in the DESTROYED state, the references are automatically removed. *@paramOwner controls the LifecycleOwner * of the observer@paramObserver Observer that receives events
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // LifecycleOwner is in the DESTROYED state, which is directly ignored
            return;
        }
        // Assemble the LifecycleBoundObserver using LifecycleOwner and Observer and add it to mObservers
        LifecycleBoundObserver wrapper = newLifecycleBoundObserver(owner, observer); ObserverWrapper Existing = mObservers. PutIfAbsent (observer, wrapper);if(existing ! =null && !existing.isAttachedTo(owner)) {
        / /! Existing.isattachedto (owner) Indicates that the owner of the observer added to the mObservers is not the owner that was passed in.
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if(existing ! =null) {
            return;// This indicates that the owner has been added to mObservers and that the owner is the owner that was passed in
        }
        owner.getLifecycle().addObserver(wrapper);
    }
Copy the code

The LifecycleOwner is DESTROYED, so it is ignored and cannot be added. Then assemble the LifecycleBoundObserver using LifecycleOwner and observer to wrap the instance wrapper, Use the putIfAbsent method observer-wrapper as key-value to add to the observer list mObservers. (putIfAbsent means that the observer is added only if it is not in the list.)

If the observer key already exists in the mObservers, but the owner in the value is not the owner passed in, the error “cannot add the same observer but different LifecycleOwner” will be reported. If it’s the same owner, just returne.

Finally, LifecycleOwner’s Lifecycle is used to add a wrapper to the observer.

Also, 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 instanceof LiveData.LifecycleBoundObserver) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if(existing ! =null) {
            return;
        }
        wrapper.activeStateChanged(true);
    }
Copy the code

Similar to observe(), except that the observer is always considered active and is not automatically removed.

3.2 Event callback

LiveData adds an observer LifecycleBoundObserver, and here’s how to do the callback:

    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(a) { // At least in the STARTED state
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);//LifecycleOwner is in the DESTROYED state, and the observer is removed
                return;
            }
            activeStateChanged(shouldBeActive());
        }

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

        @Override
        void detachObserver(a) {
            mOwner.getLifecycle().removeObserver(this); }}Copy the code

LifecycleBoundObserver is an inner class of LiveData and is a wrapper around the original Observer, binding the LifecycleOwner and Observer together. When LifecycleOwner is active, LifecycleBoundObserver is an active observer.

It implements the self-interface LifecycleEventObserver and implements the onStateChanged method. The last Lifecycle article mentioned onStateChanged as a callback for Lifecycle state changes.

When the LifecycleOwner life cycle state changes, determine if the state is DESTROYED and remove the observer. This is where LiveData automatically removes observers. If the state is not DESTROYED, call the activeStateChanged() method of the superclass ObserverWrapper to handle this lifecycle state change, shouldBeActive() as an argument, at least if the STARTED state is true. That is, the active state is true.

    private abstract class ObserverWrapper {...void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;// If there is no change in the active state, it is not processed.
            }
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;// There are no active observers
            LiveData.this.mActiveCount += mActive ? 1 : -1;// If mActive is true, the system becomes active
            if (wasInactive && mActive) {
                onActive();// The number of active observers goes from 0 to 1
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive(); // The number of active observers changed from 1 to 0
            }
            if (mActive) {
                dispatchingValue(this);// When the observer becomes active, the data is distributed}}}Copy the code

ObserverWrapper is also an inner class of LiveData. MActive is a property of ObserverWrapper that indicates whether this observer is active. If the active state has not changed, it is not processed.

LiveData. This. MActiveCount = = 0 is refers to the number of active observer LiveData. Changes in the number of active observers from 0 to 1 and from 1 to 0 call the onActive() and onInactive() methods of LiveData, respectively. This is the callback method used by the extension mentioned earlier.

Finally, the observer becomes active and the data is distributed using LiveData’s dispatchingValue(observerWrapper) :

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;// If the current distribution is in progress, the distribution is invalid, return
            return;
        }
        mDispatchingValue = true; // The tag is being distributed
        do {
            mDispatchInvalidated = false; 
            if(initiator ! =null) {
                considerNotify(initiator); //observerWrapper is not null, use notice () to notify the real observer
                initiator = null;
            } else { // The observerWrapper is empty and traversal notifies all observers
                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

If the current distribution is in progress, the distribution is invalid. If observerWrapper is not empty, use notice () to notify the real observer, if observerWrapper is empty, let the loop notify all observers. When is observerWrapper empty? Here’s a question. Let’s continue with the notice () method:

    private void considerNotify(ObserverWrapper observer) {
        if(! observer.mActive) {return; // The observer is inactive return
        }
        // If the owner of the current observer is not active, the activeStateChanged method is called again with false and its internal judgment is repeated
        if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);// Call back to the real mObserver onChanged method
    }
Copy the code

Check the status first: return if the observer is inactive; If the owner corresponding to the current observer is inactive, the activeStateChanged method is called again with false and its internal judgment is repeated. Finally, call back to the onChanged method of the real mObserver, with the value being the LivaData variable mData.

And that’s where the logic of the callback works.

3.3 Data Update

You can use setValue(value) and postValue(value) to update LivaData data. The difference is that postValue(value) is used for child threads:

//LivaData.java
    private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run(a) {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            setValue((T) newValue); // Also go to the setValue method}};protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if(! postTask) {return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);// Throw to the main thread
    }
Copy the code

The postValue method throws the Runable mPostValueRunnable onto the main thread, and its run method uses setValue() again. Here we go:

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

SetValue () assigns value to mData and then calls dispatchingValue(NULL) with the argument null, which corresponds to the empty observerWrapper scenario mentioned earlier, traversing all observers to distribute the callback.

Here the full implementation logic of the observer mode becomes clear: LivaData adds an observer bound to LifecycleOwner with observe(); Call back the latest data when the observer becomes active; All observers are notified when data is updated with setValue() and postValue().

3.4 principle of Transformations

In the end, let’s look at the map principle of doubling and how data modification is implemented. Similar to switchMap.

//Transformations.java
    public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,@NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) { result.setValue(mapFunction.apply(x)); }});return result;
    }
Copy the code

New creates a MediatorLiveData instance, then calls the addSource method with the passed livaData, new Observer instance as arguments:

//MediatorLiveData.java
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
        Source<S> e = newSource<>(source, onChanged); Source<? > existing = mSources.putIfAbsent(source, e);if(existing ! =null&& existing.mObserver ! = onChanged) {throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        }
        if(existing ! =null) {
            return;
        }
        if (hasActiveObservers()) {
        //MediatorLiveData has active observer, just pluge.plug(); }}Copy the code

MediatorLiveData is a subclass of LiveData that looks at other LiveData and responds to its OnChanged callback. The incoming livaData and Observer are wrapped as Source instances and added to the list mSources.

If MediatorLiveData has active observers, call Plug () :

//MediatorLiveData.java
    private static class Source<V> implements Observer<V> {
        final LiveData<V> mLiveData;
        final Observer<? super V> mObserver;
        int mVersion = START_VERSION;

        Source(LiveData<V> liveData, final Observer<? super V> observer) {
            mLiveData = liveData;
            mObserver = observer;
        }

        void plug(a) {
            mLiveData.observeForever(this);//observeForever
        }

        void unplug(a) {
            mLiveData.removeObserver(this);
        }

        @Override
        public void onChanged(@Nullable V v) {
            if(mVersion ! = mLiveData.getVersion()) { mVersion = mLiveData.getVersion(); mObserver.onChanged(v);// Call back to the incoming LiveData when the source LiveData changes}}}Copy the code

Source is the inner class of MediatorLiveData, which is the wrapper around the Source LiveData. In plug(), let the source LiveData call the observeForever method to add the forever observer – itself. The observeForever method is used here because the source LiveData does not call the Observer method to add observers when it is used externally. The observeForever method is used to call back to the mobserver.onChanged (v) method when the source LiveData data changes. This is the nChanged method in the Doubling map method. Before e.lug (), there is a judgment that MediatorLiveData confirms that there are active observers.

SetValue (mapfunction.apply (x)) for the MediatorLiveData instance is called in the nChanged method in the map method. And return the instance. Mapfunction.apply () is the modification logic Function instance passed in by the map method.

Final class diagram:

Four,

This article first introduces the concept of LiveData — using observers and life cycle awareness, and then usage patterns, customization of LivaData, and advanced usage. Finally, the source code and principle of LiveData are analyzed in detail.

And you can see how Lifecycle plays a role in LiveData and understand the important use of the observer pattern. LiveData was the core of our subsequent MVVM architecture. LiveData is also something we must master and understand.

The next article will cover viewModels, which are also at the heart of AAC. That’s all for today

.

Thanks and reference:

Livedata official document

Android Jetpack Architecture Components (part 5)

.

Your likes and comments are a great encouragement to me!

Welcome to follow my public account, wechat search Hu Feiyang, article updates can be the first time to receive.