1. The background
In the last article, we looked at the source code for the Lifecycles component. In this article, we’ll look at the LiveData component
Related articles:
1. Jetpack source code parsing — you know what Navigation is?
2. Jetpack source code analysis – Why does Navigation switch Fragment redraw?
3. Jetpack source code parsing — Manage the life cycle with Lifecycles
Basis of 2.
2.1 introduction
LiveData is an observable data holder class. Unlike a regular Observable, LiveData is lifecycle aware, which means it respects the lifecycle of other application components, such as activities, fragments, or services. This awareness ensures that LiveData updates only observers of application components that are in the active lifecycle state.
2.2 the advantages
1. Ensure that the UI conforms to data state LiveData follows observer mode. LiveData notifies the Observer when the lifecycle state changes. You can incorporate code to update the UI into these Observers. Regardless of the timing of the data changes, the Observer updates the UI each time the data changes.
2. No memory leaks The Observer binds objects with a lifetime and cleans up after the bound object is destroyed.
If the Observer life cycle is inactive, such as an Activity in the back stack, it will not be notified of any LiveData events.
4. There is no need to manually handle life cycle UI components and only need to observe relevant data. There is no need to manually stop or resume observation. LiveData automatically manages these things because as it watches, it senses changes in the life cycle of the corresponding component.
5. Always Keep data up to date If an object’s life cycle goes inactive, it will receive the latest data when it becomes active again. For example, a background Activity receives the latest data immediately after it returns to the foreground.
If an Activity or Fragment is recreated due to a configuration change (such as a device rotation), it will immediately receive the latest available data.
7. Sharing resources You can use the singleton pattern to extend LiveData objects and wrap them as system services for sharing within applications. Once the LiveData object is connected to a system service, any Observer that requires the resource simply observes the LiveData object.
2.3 Basic Usage
Demo is used in our Jetpack_Note, please check LiveDataFragment for details. The Demo prints values in the console by listening for the life cycle of a LiveData object. First declare a LiveData object:
private lateinit var liveData: MutableLiveData<String>
Copy the code
Click the “Start View data” button and the console pops up. We can see that the console outputs the onStart() log, because we have bound the value of liveData to the Fragment’s life cycle. When we return to the desktop or destroy the Fragment, the value of liveData will become the corresponding life cycle function. And print it in the console:
class LiveDataFragment : Fragment() {
private lateinit var liveData: MutableLiveData<String>
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup? , savedInstanceState:Bundle?).: View? {
return inflater.inflate(R.layout.fragment_live_data, container, false)}override fun onActivityCreated(savedInstanceState: Bundle?). {
super.onActivityCreated(savedInstanceState)
liveData = MutableLiveData()
btn_observer_data.setOnClickListener {
if (FloatWindow.get() = =null) { FloatWindowUtils.init(activity? .application!!) } FloatWindowUtils.show()// Create an observer to update the UI
val statusObserver = Observer<String> { lifeStatus ->
FloatWindowUtils.addViewContent("LiveData-onChanged: $lifeStatus")
}
liveData.observeForever(statusObserver)
}
}
override fun onStart(a) {
super.onStart()
liveData.value = "onStart()"
}
override fun onPause(a) {
super.onPause()
liveData.value = "onPause()"
}
override fun onStop(a) {
super.onStop()
liveData.value = "onStop()"
}
override fun onDestroy(a) {
super.onDestroy()
liveData.value = "onDestroy()"}}Copy the code
** Note: ** HERE I use observeForever to listen for all lifecycle methods, so you’ll see the print of lifecycle functions like onDestroy().
Ok, Demo is very simple, let’s take a look at the source code, for analysis:
3. Source code analysis:
3.1 the observer ()
We declare a LiveData object and listen for the Fragment life cycle to change the value in LiveData. LiveData is actually like a container. The Demo stores a String value. You can listen for his changes in the callback. Let’s start with addObserver:
@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
When we call observer(), we pass two parameters. The first is an instance of the LifecycleOwner interface. The Fragment we inherit already implements this interface, so we pass this. The second parameter Observer is the callback we observed. We then pass these two parameters new to create a new object: LifecycleBoundObserver, and finally LifecycleBoundObserver and LifecycleOwner are bound, LifecycleOwner can be interpreted as an instance of our Fragment or Activity, because they both implement LifecycleOwner.
3.2 LifecycleBoundObserver
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {...@Override
boolean shouldBeActive(a) {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
// Callback method when the Activity lifecycle changes
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
// Update liveData active status
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
// Remove the listener
@Override
void detachObserver(a) {
mOwner.getLifecycle().removeObserver(this); }}Copy the code
We can see that the LifecycleOwner is bound and onStateChanged is implemented, activeStateChanged(shouldBeActive()) is executed when the lifecycle changes; Methods; ShouldBeActive () returns the requirement that the life cycle be at least STARTED to be considered a Activie state; Unbind LifecycleOwner and LiveData if state is DESTROYED. Let’s look at how to update data values in LiveData:
3.3 dispatchingValue ()
We trace the activeStateChanged method and find that we have done something inside that is active with the state value, and update the data value when the state is active:
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this); }}Copy the code
void dispatchingValue(@Nullable ObserverWrapper initiator) {...// All observers traversing LiveData execute the following code
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break; }}... }// Call back the data value to livedata.observer()
private void considerNotify(ObserverWrapper observer) {
if(! observer.mActive) {return;
}
if(! observer.shouldBeActive()) { observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
Copy the code
From the above we can see the whole process of data update and data callback of LiveData, but what happens when we manually setValue()? You’ll notice that setValue() actually ends up calling the dispatchingValue() method. PostValue () updates data in the child thread using this Handler.
The code in LiveData is concise, with over 400 lines of code, and it doesn’t seem like a lot of work. Here’s a look at the process:
- Through the use of
LiveData
Object for which an observer is createdObserver
- create
Observer
When bindingFragment
The life cycle LifecycleBoundObserver
As the life cycle changes,dispatchValue
Issued by the updateLiveData
The values in the- when
LiveData
Take the initiative tosetValue
Will take the initiativedispatchValue
And willconsiderNotify
The activationobserver
4. The extension
4.1 the Map transformation
Lifecycle packages provide you with a brace of classes that you can perform in a quick way:
A function is used to convert the values stored in the LiveData object and pass the converted values downwards by ** youtu.map () :
LiveDataViewModel
class LiveDataViewModel : ViewModel() {
val data = listOf(User(0."Hankkin"), User(1."Tony"),User(2."Bob"),User(3."Lucy"))
val id = MutableLiveData<Int> ()// Map conversion returns the User entity
val bean: LiveData<User> = Transformations.map(id, Function {
return@Function findUserById(id.value!!)
})
// Find User by id
private fun findUserById(id: Int): User? {
return data.find { it.id == id }
}
}
Copy the code
LiveDataFragment
// Change the value of idLiveData in ViewModel
btn_observer_map.setOnClickListener {
mId++
viewModel.id.postValue(mId)
}
// When idLiveData changes, the UserBean also changes and updates the text of the Textview
viewModel.bean.observe(
this,
Observer { tv_livedata_map.text = if (it == null) "User not found" else "The User found for you is:${it.name}" })
Copy the code
4.2 the Map source
@MainThread
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
The first parameter is the LiveData value that we need to change, that is, the userID in the above example. The second parameter is the function we passed through the higher-order function. Set the value to LiveData.
Let’s look at the addSource() method:
@MainThread
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()) { e.plug(); }}Copy the code
Here we wrap our LiveData and Observer objects into Source objects, and this object cannot be added repeatedly. For details, see Source:
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);
}
void unplug(a) {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if(mVersion ! = mLiveData.getVersion()) { mVersion = mLiveData.getVersion(); mObserver.onChanged(v); }}}Copy the code
First, the Source is an Observer, and as you can see, the Observer we use externally is added to the incoming LiveData as a member variable of the Source. Note that mlivedata.observeforever (this) is used; .
As you can see from the use of observeForever (), we are not passing LifecycleOwner, so it does not have life awareness. This means that the given observer will receive all events and will never be automatically removed.
The principle of map() is based on MediatorLiveData, which internally encapsulates passed LiveData and observers into internal classes and then places them in a internally maintained map. And it automatically completes observeForever() and removeObserver() for us.
5. To summarize
LiveData
Implementation based on the observer pattern, andLifecycleOwner
To bind, andLifecycleOwner
Has beenFragment
And Activity implementation, so it can sense the lifecycle; The current LifecycleOwner is not active (for exampleonPasue()
,onStop()
), LiveData is not called backobserve()
Because there is no point.- At the same time
LiveData
Only inLifecycleOwner
In aActive
If the data change occurs in the inactive state, the data changes, but no notification is sent, etcowner
Return to the active state and send a notification. LiveData
They are removed during DESTROYEDObserver
Unsubscribe and there will be no memory leakpostValue
In asynchronous threads,setValue
In the main thread- if
LiveData
Has not beenobserve()
Then you call the postValue(…) of this LiveData. /value=… , it isIt doesn’t do anything
Of course, it is officially recommended that we use LiveData together with ViewModel, because LiveData generally appears in ViewModel, so we will continue to analyze ViewModel in the next article.