This article has been authorized to be reprinted by guolin_blog, an official wechat account.
This article is mainly for LiveData source analysis, it is recommended to read the article against the sample code, the sample code is as follows:
LiveDataDemo
This article uses the source code analysis of Android SDK 29.
define
LiveData is an observable data store class with lifecycle awareness that follows application components (e.g. The lifecycle of activities, fragments, and Services (you can use LifecycleService, which implements the LifecycleOwner interface). This awareness ensures that LiveData updates only application component observers that are in an active lifecycle state.
If an Observer (represented by an Observer class) is in a STARTED or RESUMED state during its lifecycle, LiveData will assume that the Observer is active and will notify it of updates, while inactive observers registered observing a LiveData object will not receive notification of changes.
Application components all implement the LifecycleOwner interface and with this relationship Lifecycle objects are removed when the state of the corresponding Lifecycle object changes to DESTROYED.
Advantages of using LiveData:
-
Ensure that the interface conforms to the data state
LiveData follows the Observer pattern, notifying observers when the lifecycle state changes and then updating the interface.
-
No memory leaks will occur
When the application component’s lifecycle is in a DESTROYED state, the observer is removed so that it no longer holds references to the application component.
-
It does not crash when the Activity stops
If the observer’s life cycle is inactive (for example, return Actvity in the stack), it will not accept any LiveData events.
-
You no longer need to handle the life cycle manually
The interface component simply observes the relevant data and does not stop or resume the observation, and LiveData automatically manages all of these operations because it senses the relevant lifecycle state changes as it observes.
-
Data is always up to date
If the life cycle becomes inactive, it will receive the latest data when it becomes active again. For example, an Activity that was in the background will receive the latest data as soon as it returns to the foreground.
-
Appropriate configuration changes
If an Activity or Fragment is recreated due to a configuration change (for example, device rotation), it will immediately receive the latest data.
-
Shared resources
We can use the singleton pattern to inherit LiveData to encapsulate related business logic so that it can be shared across applications.
Using LiveData
The steps are as follows:
- Typically, a LiveData instance is created in a ViewModel to store some type of data.
- Create an Observer object and implement its onChanged method, which controls what to process when the data stored by the LiveData object changes.
- In most cases, the LiveData object is observed in the onCreate method of the application component, and the Observer method of the LiveData object is called with two parameters. The first parameter is LifecycleOwner, which is an interface, The Activity, Fragment, and LifecycleService all implement this interface, and the second parameter is the Observer created in step 2, so that the Observer can subscribe to the LiveData object to be notified of changes.
Note: Make sure that the LiveData object used to update the interface is stored in a ViewModel object, not in an Activity or Fragment, for the following reasons:
- Avoid activities or fragments that are too large because these interface controllers are only responsible for displaying data, not storing data state.
- Separating the LiveData instance from activities and fragments allows it to continue to exist after configuration changes.
The sample code
Add the following dependencies to the project:
implementation 'androidx. Lifecycle: lifecycle - extensions: 2.2.0 - alpha02'
implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines -- core: 1.3.0'
implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.3.0'
implementation 'androidx. Lifecycle: lifecycle - viewmodel - KTX: 2.2.0 - rc03'
Copy the code
Since I’m using DataBinding, I add the following code:
dataBinding {
enabled = true
}
Copy the code
The project structure is as follows:
Define MainViewModel and inherit from ViewModel, create two MutableLiveData in this class, firstContent is to demonstrate updating the LiveData object in the main thread, SecondContent is used to demonstrate updating a LiveData object on a worker thread as follows:
/** * Created by TanJiaJun on 2019-12-22. */
class MainViewModel : ViewModel() {
private val _firstContent = MutableLiveData<String>().apply {
value = "The first text"
}
val firstContent: LiveData<String> = _firstContent
private val _secondContent = MutableLiveData<String>().apply {
value = "The second text"
}
val secondContent: LiveData<String> = _secondContent
// Update the LiveData object in the main thread, calling the setValue method of MutableLiveData, which will be analyzed below
fun changeFirstContent(text: String) {
_firstContent.value = text
}
// Update the LiveData object in the worker thread, calling the postValue method of MutableLiveData, which will be analyzed below
fun changeSecondContent(text: String) =
viewModelScope.launch {
withContext(Dispatchers.Default) {
_secondContent.postValue(text)
}
}
}
Copy the code
Define FirstFragment as follows:
/** * Created by TanJiaJun on 2019-12-22. */
class FirstFragment : Fragment(), FirstHandlers {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup? , savedInstanceState:Bundle?).: View? =
DataBindingUtil.inflate<FragmentFirstBinding>(
inflater,
R.layout.fragment_first,
container,
false
)
.also { binding ->
// The setLifecycleOwner method is called, as discussed below
binding.lifecycleOwner = this
// Bind the MainViewModel to the viewbinding.viewModel = activity? .let { ViewModelProviders.of(it)[MainViewModel::class.java]
}
binding.handlers = this
}
.root
// Click Button to navigate to SecondFragment
override fun onNavigateToSecondFragmentClick(view: View) {
(activity as MainActivity).addFragment(SecondFragment(), FRAGMENT_TAG_SECOND)
}
}
interface FirstHandlers {
fun onNavigateToSecondFragmentClick(view: View)
}
Copy the code
Define SecondFragment as follows:
/** * Created by TanJiaJun on 2019-12-22. */
class SecondFragment : Fragment(), SecondHandlers {
private var viewModel: MainViewModel? = null
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState) viewModel = activity? .let { ViewModelProviders.of(it)[MainViewModel::class.java]}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup? , savedInstanceState:Bundle?).: View? =
DataBindingUtil.inflate<FragmentSecondBinding>(
inflater,
R.layout.fragment_second,
container,
false
)
.also {
it.handlers = this
}
.root
// Click on the first Button to call the changeFirstContent method in MainViewModel. Change the text of the first TextView in the FirstFragment from "the first text" to "the first text changed".
override fun onChangeFirstContentClick(view: View){ viewModel? .changeFirstContent(getString(R.string.first_content_changed)) }// Click on the second Button to call the changeSecondContent method in MainViewModel. Change the text of the second TextView in the FirstFragment from "second text" to "second text changed".
override fun onChangeSecondContentClick(view: View){ viewModel? .changeSecondContent(getString(R.string.second_content_changed)) } }interface SecondHandlers {
fun onChangeFirstContentClick(view: View)
fun onChangeSecondContentClick(view: View)
}
Copy the code
Since I’m also using DataBinding, ViewModel, and Kotlin coroutines, I’ll start with the source code as written in the official documentation below.
Source code analysis
LiveData is an abstract class. We mainly use MutableLiveData and MediatorLiveData as two subclasses. Let’s first look at the structure diagram of LiveData:
Let’s take a look at a few common methods, starting with the Observer method, which is used to add specified observers to the watch list in the specified LifecycleOwner, as follows:
// LiveData.java
// Create a Map with key Observer and value ObserverWrapper
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// Check if it is on the main thread. If it is not, an exception will be thrown
assertMainThread("observe");
// Check whether LifecycleOwner is in the DESTROYED state
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// If yes, return
return;
}
// Create LifecycleBoundObserver and wrap the LifecycleOwner and Observer passed in. This class will be examined below
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// Store LifecycleBoundObserver in SafeIterableMap with key as Observer and value as ObserverWrapper, and return null if the Observer is stored for the first time
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// The first condition is to determine whether the Observer is stored for the first time, and the second condition is to determine whether the Observer is already attached to the LifecycleOwner
if(existing ! =null && !existing.isAttachedTo(owner)) {
// If the Observer is not stored for the first time and is already attached to LifecycleOwner, throws "cannot add an attachment to a different lifecycle, but is the same Observer
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
// Again determine whether to store the Observer for the first time
if(existing ! =null) {
// Return if the Observer is not stored for the first time
return;
}
Lifecycle calls the addObserver method
owner.getLifecycle().addObserver(wrapper);
}
Copy the code
SafeIterableMap is a Map, which is actually a LinkedList and can be modified during iterations, but is not thread-safe, and LiveData uses it to store LifecycleBoundObserver.
Lifecycle is an abstract class whose only subclass is LifecycleRegistry. Lifecycle is an abstract class whose only subclass is LifecycleRegistry.
// LifecycleRegistry.java
// Create a FastSafeIterableMap with key as LifecycleObserver and value as ObserverWithState
private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
new FastSafeIterableMap<>();
// Providers that own this Lifecycle only keep weak references in LifecycleOwner, so if someone leaks a Lifecycle object, they won't leak the entire Activity or Fragment, but doing so will also leak all Listener objects, Because strong references are maintained on all Listener objects
private final WeakReference<LifecycleOwner> mLifecycleOwner;
public LifecycleRegistry(@NonNull LifecycleOwner provider) {
mLifecycleOwner = new WeakReference<>(provider);
mState = INITIALIZED;
}
@Override
public void addObserver(@NonNull LifecycleObserver observer) {
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
// Wrap LifecycleObserver (we passed LifecycleBoundObserver) and State as ObserverWithState
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
// Store ObserverWithState in FastSafeIterableMap, key is LifecycleBoundObserver, value is ObserverWithState, If you store this object with State for the first time, null is returned
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
// Determine whether to store the ObserverWithState for the first time
if(previous ! =null) {
// Return if it is not the first time the ObserverWithState is stored
return;
}
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
// Check whether LifecycleOwner is null
if (lifecycleOwner == null) {
// If it is null, it should be destroyed immediately
return;
}
booleanisReentrance = mAddingObserverCounter ! =0 || mHandlingEvent;
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
// mState or subling may be recalculated
targetState = calculateTargetState(observer);
}
// Check whether it is at the top of the stack
if(! isReentrance) {// Perform synchronization if it is at the top of the stack
sync();
}
mAddingObserverCounter--;
}
Copy the code
If it is at the top of the stack, the sync method is called, so it will not be called on reentrant. Let’s look at this method, which looks like this:
// LifecycleRegistry.java
private void sync(a) {
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
// Check whether LifecycleOwner is null
if (lifecycleOwner == null) {
// If null, throw an exception proving that the LifecycleRegistry LifecycleOwner has been garbage collected and it is too late to change the lifecycle
throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
+ "garbage collected. It is too late to change lifecycle state.");
}
while(! isSynced()) { mNewEventOccurred =false;
; // No need to check whether John is null because isSynced already determines
if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
// If the state of ObserverWithState is less than the current state, the state changes in reverse order and the observer is notified
backwardPass(lifecycleOwner);
}
Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
if(! mNewEventOccurred && newest ! =null
&& mState.compareTo(newest.getValue().mState) > 0) {
// If the state of ObserverWithState is greater than the current state, the state is changed in order and the observer is notified
forwardPass(lifecycleOwner);
}
}
mNewEventOccurred = false;
}
Copy the code
Let’s look at the forwardPass and backwardPass methods. The code looks like this:
// LifecycleRegistry.java
// Positive traversal changes the state and notifies the observer
private void forwardPass(LifecycleOwner lifecycleOwner) {
Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
mObserverMap.iteratorWithAdditions();
// Traversal the mObserverMap in positive order
while(ascendingIterator.hasNext() && ! mNewEventOccurred) { Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next(); ObserverWithState observer = entry.getValue();while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
&& mObserverMap.contains(entry.getKey()))) {
pushParentState(observer.mState);
// Call the dispatchEvent method with ObserverWithStateobserver.dispatchEvent(lifecycleOwner, upEvent(observer.mState)); popParentState(); }}}// Iterate backwards to change the state while notifying the observer
private void backwardPass(LifecycleOwner lifecycleOwner) {
Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
mObserverMap.descendingIterator();
// Iterate backwards through the mObserverMap
while(descendingIterator.hasNext() && ! mNewEventOccurred) { Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next(); ObserverWithState observer = entry.getValue();while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
&& mObserverMap.contains(entry.getKey()))) {
Event event = downEvent(observer.mState);
pushParentState(getStateAfter(event));
// Call the dispatchEvent method with ObserverWithStateobserver.dispatchEvent(lifecycleOwner, event); popParentState(); }}}Copy the code
ObserverWithState is a static inner class for LifecycleRegistry. Let’s look at the dispatchEvent method for ObserverWithState, which looks like this:
// LifecycleRegistry.java
static class ObserverWithState {
State mState;
LifecycleEventObserver mLifecycleObserver;
ObserverWithState(LifecycleObserver observer, State initialState) {
/ / mLifecycleObserver LifecycleBoundObserver
mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
mState = initialState;
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = getStateAfter(event);
mState = min(mState, newState);
// Call onStateChanged method of LifecycleBoundObservermLifecycleObserver.onStateChanged(owner, event); mState = newState; }}Copy the code
LifecycleBoundObserver is an internal class of LiveData, so let’s look at onStateChanged.
// LiveData.java
// Lifecycle determines whether Lifecycle is greater than or equal to the value of the STARTED state and thus whether Lifecycle is active
@Override
boolean shouldBeActive(a) {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
Lifecycle is in a DESTROYED state
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
Lifecycle is DESTROYED, and return. The removeObserver method will be analyzed below
removeObserver(mObserver);
return;
}
// If Lifecycle is not in a DESTROYED state, call activeStateChanged and pass in the Boolean value derived from shouldBeActive
activeStateChanged(shouldBeActive());
}
Copy the code
State is an enumeration, which looks like this:
@SuppressWarnings("WeakerAccess")
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
STARTED,
RESUMED;
// Determine whether the current state value is greater than or equal to the value of the passed state
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0; }}Copy the code
So we can see that the only values that are greater than or equal to STARTED are STARTED and RESUMED, so this method is to determine whether or not we’re active.
The activeStateChanged method code looks like this:
// LiveData.java
void activeStateChanged(boolean newActive) {
// Determine whether the state has changed
if (newActive == mActive) {
// Return if there is no change
return;
}
// If something changes, set the status immediately so that no content is sent to inactive observers
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
// If the current state is active, it increases by 1, otherwise it decreases by 1
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
// If mActiveCount changes from 0 to 1 and the current state is active, onActive is called
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
// If mActiveCount changes from 1 to 0 and the current state is inactive, onInactive is called
onInactive();
}
if (mActive) {
// If the current state is active, call dispatchingValue and pass in LifecycleBoundObserver
dispatchingValue(this); }}Copy the code
Let’s look at the dispatchingValue method as follows:
// LiveData.java
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// Determine whether distribution is in progress
if (mDispatchingValue) {
// If distribution is taking place, set mDispatch validated to true and return
mDispatchInvalidated = true;
return;
}
// It is being distributed
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// Check whether the initiator is null. According to the above code, it is not null. Pay attention to the null logic, which is involved in setValue, postValue, and getValue
if(initiator ! =null) {
If the initiator is not null, the Duties method is called and LifecycleBoundObserver is passed in
considerNotify(initiator);
initiator = null;
} else {
// If initiator is null, mObservers will be traversed
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// call the notice method
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break; }}}}while (mDispatchInvalidated);
// Distribution completed
mDispatchingValue = false;
}
Copy the code
The two variables mDispatchingValue and mDispatchInvalidated are used to prevent repeated distribution of the same content.
Notice method code is as follows:
// LiveData.java
@SuppressWarnings("unchecked")
private void considerNotify(ObserverWrapper observer) {
// Check whether it is active
if(! observer.mActive) {// Return if it is not active
return;
}
// Check whether the latest state is active before distribution, in case the state has changed, but we have not received the event
if(! observer.shouldBeActive()) {// Notifications will not be distributed if they are not active
observer.activeStateChanged(false);
return;
}
// Check whether it is the latest data
if (observer.mLastVersion >= mVersion) {
// If the data is up to date, it will not be distributed
return;
}
// Set the version to the latest version
observer.mLastVersion = mVersion;
// Call the onChanged method
observer.mObserver.onChanged((T) mData);
}
Copy the code
The onChanged method has the following code:
// Observer.java
public interface Observer<T> {
void onChanged(T t);
}
Copy the code
The onChanged method is the one we need to implement in order to implement the logic to be executed when the data changes.
Do you remember sync? If you forgot, look up again, the sync method is also called by the moveToState method, as follows:
// LifecycleRegistry.java
private void moveToState(State next) {
if (mState == next) {
return;
}
mState = next;
// Determine whether an observer is being added or synchronized
if(mHandlingEvent || mAddingObserverCounter ! =0) {
mNewEventOccurred = true;
// we will figure out what to do on upper level.
// Return if synchronizing or adding observers
return;
}
mHandlingEvent = true;
// Call sync
sync();
mHandlingEvent = false;
}
Copy the code
The moveToState method is also called by the handleLifecycleEvent method, which changes the lifecycle state and notifies the observer that calling this method has no effect if the current lifecycle state is the same as the last time the method was called:
// LifecycleRegistry.java
public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
State next = getStateAfter(event);
moveToState(next);
}
Copy the code
So when is the LifecycleRegistry object created? What does it do? In our example code, MainActivity inherits AppCompatActivity, which inherits FragmentActivity, Androidx.activity.Com ponentActivity FragmentActivity inheritance, we look at the androidx.activity.Com ponentActivity code:
// androidx.activity.ComponentActivity.java
// Create a LifecycleRegistry object
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedStateRegistryController.performRestore(savedInstanceState);
/ / injection ReportFragment
ReportFragment.injectIfNeededIn(this);
if(mContentLayoutId ! =0) { setContentView(mContentLayoutId); }}Copy the code
Let’s look at the ReportFragment code as follows:
// ReportFragment.java
private static final String REPORT_FRAGMENT_TAG = "androidx.lifecycle"
+ ".LifecycleDispatcher.report_fragment_tag";
public static void injectIfNeededIn(Activity activity) {
ProcessLifecycleOwner should always work correctly. Some activities may not inherit FragmentActivity from Support Lib, so use FragmentActivity from the Framework
android.app.FragmentManager manager = activity.getFragmentManager();
if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
// Hopefully, we are the first to make a transaction.manager.executePendingTransactions(); }}@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
dispatchCreate(mProcessListener);
// The dispatch method is called, passing in Lifecycle.event.on_create
dispatch(Lifecycle.Event.ON_CREATE);
}
@Override
public void onStart(a) {
super.onStart();
dispatchStart(mProcessListener);
// Call the dispatch method, passing in Lifecycle.Event.ON_START
dispatch(Lifecycle.Event.ON_START);
}
@Override
public void onResume(a) {
super.onResume();
dispatchResume(mProcessListener);
// Call the dispatch method, passing in Lifecycle.Event.ON_RESUME
dispatch(Lifecycle.Event.ON_RESUME);
}
@Override
public void onPause(a) {
super.onPause();
// Call the dispatch method, passing in Lifecycle.Event.ON_PAUSE
dispatch(Lifecycle.Event.ON_PAUSE);
}
@Override
public void onStop(a) {
super.onStop();
// Call the dispatch method, passing in Lifecycle.Event.ON_STOP
dispatch(Lifecycle.Event.ON_STOP);
}
@Override
public void onDestroy(a) {
super.onDestroy();
// Call the dispatch method, passing in Lifecycle.Event.ON_DESTROY
dispatch(Lifecycle.Event.ON_DESTROY);
// Ensure that references to an Activity are not disclosed
mProcessListener = null;
}
private void dispatch(Lifecycle.Event event) {
Activity activity = getActivity();
// Check whether the Activity implements the LifecycleRegistryOwner interface
if (activity instanceof LifecycleRegistryOwner) {
// Call the handleLifecycleEvent method
((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
return;
}
// Check whether the Activity implements the LifecycleOwner interface
if (activity instanceof LifecycleOwner) {
Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
Lifecycle is a subclass of LifecycleRegistry
if (lifecycle instanceof LifecycleRegistry) {
// Call the handleLifecycleEvent method((LifecycleRegistry) lifecycle).handleLifecycleEvent(event); }}}Copy the code
LifecycleRegistry’s handleLifecycleEvent method is called whenever the Activity’s lifecycle changes. The moveToState method is called and the sync method is called to make LiveData aware of the Lifecycle of the Activity. This is another important component of Android Jetpack that Lifecycle works. Let’s also look at the Fragment code:
// Fragment.java
LifecycleRegistry mLifecycleRegistry;
@Nullable FragmentViewLifecycleOwner mViewLifecycleOwner;
MutableLiveData<LifecycleOwner> mViewLifecycleOwnerLiveData = new MutableLiveData<>();
private void initLifecycle(a) {
// Create a LifecycleRegistry object
mLifecycleRegistry = new LifecycleRegistry(this);
mSavedStateRegistryController = SavedStateRegistryController.create(this);
// Check whether the current Android version is larger than 4.4
if (Build.VERSION.SDK_INT >= 19) {
mLifecycleRegistry.addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
// Check whether the current event is ON_STOP
if (event == Lifecycle.Event.ON_STOP) {
// Check whether the View is null
if(mView ! =null) {
If not empty, cancel any delayed high-level input events previously published to the event queuemView.cancelPendingInputEvents(); }}}}); }}void performCreate(Bundle savedInstanceState) {
// Omit some code
// Call handleLifecycleEvent, passing in Lifecycle.Event.ON_CREATE
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
void performCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mChildFragmentManager.noteStateNotSaved();
mPerformedCreateView = true;
// Create the Fragment View LifecycleOwner
mViewLifecycleOwner = new FragmentViewLifecycleOwner();
mView = onCreateView(inflater, container, savedInstanceState);
// Check whether the View is null
if(mView ! =null) {
// Create the View Lifecycle of the Fragment
mViewLifecycleOwner.initialize();
// Then notify some observers of the new LifecycleOwner
mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner);
} else {
/ / determine whether FragmentViewLifecycleOwner initialization
if (mViewLifecycleOwner.isInitialized()) {
/ / if the View is null, and FragmentViewLifecycleOwner initialization, throw an exception
throw new IllegalStateException("Called getViewLifecycleOwner() but "
+ "onCreateView() returned null");
}
mViewLifecycleOwner = null; }}void performStart(a) {
// Omit some code
// Call onStart
onStart();
// Omit some code
LifecycleRegistry calls the handleLifecycleEvent method, passing in Lifecycle.event.ON_START
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
// Check whether the View is null
if(mView ! =null) {
/ / if the View is not null, call FragmentViewLifecycleOwner handleLifecycleEvent, incoming Lifecycle. Event. ON_START
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
// Omit some code
}
void performResume(a) {
// Omit some code
// Call onResume
onResume();
// Omit some code
LifecycleRegistry calls the handleLifecycleEvent method, passing in Lifecycle.Event.ON_RESUME
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
// Check whether the View is null
if(mView ! =null) {
/ / if the View is not null, call FragmentViewLifecycleOwner handleLifecycleEvent, incoming Lifecycle. Event. ON_RESUME
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
}
// Omit some code
}
void performPause(a) {
// Omit some code
// Check whether the View is null
if(mView ! =null) {
/ / if the View is not null, call FragmentViewLifecycleOwner handleLifecycleEvent method, was introduced into Lifecycle. Event. ON_PAUSE
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
}
LifecycleRegistry calls the handleLifecycleEvent method, passing in Lifecycle.Event.ON_PAUSE
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
// Omit some code
// Call onPause
onPause();
// Omit some code
}
void performStop(a) {
// Omit some code
// Check whether the View is null
if(mView ! =null) {
/ / if the View is not null, call FragmentViewLifecycleOwner handleLifecycleEvent method, was introduced into Lifecycle. Event. ON_STOP
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
}
LifecycleRegistry calls the handleLifecycleEvent method, passing in Lifecycle.event.ON_STOP
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
// Omit some code
// Call onStop
onStop();
// Omit some code
}
void performDestroyView(a) {
// Check whether the View is null
if(mView ! =null) {
/ / if the View is not null, call FragmentViewLifecycleOwner handleLifecycleEvent method, was introduced into Lifecycle. Event. ON_DESTROY
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
}
// Omit some code
onDestroyView();
// Omit some code
}
void performDestroy(a) {
// Omit some code
LifecycleRegistry calls the handleLifecycleEvent method, passing in Lifecycle.event.ON_DESTROY
mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
// Omit some code
// Call onDestroy
onDestroy();
// Omit some code
}
Copy the code
FragmentViewLifecycleOwner represents the fragments of the View of the life cycle, in most cases, this reflects the life cycle of the fragments itself, but the fragments of life cycle will certainly longer than its View of the life cycle.
Similarly, whenever the Fragment’s life cycle changes, Is called LifecycleRegistry handleLifecycleEvent method and FragmentViewLifecycleOwner handleLifecycleEvent method, and then will call moveToState method, Finally, the sync method is called to make LiveData aware of the Fragment’s life cycle.
Then we look at the removeObserver method, which looks like this:
// LiveData.java
// This method is called on the main thread
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
// Check whether it is in the main thread
assertMainThread("removeObserver");
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
// Return if null
return;
}
// Call the detachObserver method of LifecycleBoundObserver
removed.detachObserver();
// Call LifecycleBoundObserver's activeStateChanged method, passing false, which is also analyzed above and won't be covered here
removed.activeStateChanged(false);
}
Copy the code
The code for the detachObserver method is as follows:
// LiveData.java
@Override
void detachObserver(a) {
// Call the removeObserver method of LifecycleRegistry
mOwner.getLifecycle().removeObserver(this);
}
Copy the code
The code for the removeObserver method is as follows:
// LifecycleRegistry.java
@Override
public void removeObserver(@NonNull LifecycleObserver observer) {
// Remove the observer
mObserverMap.remove(observer);
}
Copy the code
As you can see from the code above, the system will also call this method to remove the observer, preventing a memory leak.
That’s how LiveData can sense the life cycle.
The observeForever method is also a common one.
// LiveData.java
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
/ / create AlwaysActiveObserver
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
/ / if the existing LiveData. LifecycleBoundObserver class instances, throw an exception
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if(existing ! =null) {
return;
}
// Call the activeStateChanged method of AlwaysActiveObserver, passing true
wrapper.activeStateChanged(true);
}
Copy the code
Take a look at the code for AlwaysActiveObserver:
// LiveData.java
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer<? super T> observer) {
super(observer);
}
// Override the shouldBeActive method and return true. This method is used to check whether the state is active or not, so it should always return true
@Override
boolean shouldBeActive(a) {
return true; }}Copy the code
ObserveForever is used to add the specified observer to the watch list, similar to calling the Observer method, but the given LifecycleOwner state is always active, which means the observer will always receive all events, so if you want to stop observing this LiveData, Call the removeObserver method manually.
Then let’s look at the source code for three more important methods: SetValue method, postValue method and getValue method, setValue method and postValue method are used to set the value of LiveData, getValue method is used to value from LiveData, the code is as follows:
// LiveData.java
final Object mDataLock = new Object();
protected void postValue(T value) {
boolean postTask;
// Synchronization locks are applied to mDataLock objects
synchronized (mDataLock) {
// postTask uses the value NOT_SET of mPendingData to determine whether Runnable needs to be added to the message queue
postTask = mPendingData == NOT_SET;
// Assign value to mPendingData
mPendingData = value;
}
// Determine whether Runnable needs to be added to the message queue
if(! postTask) {Return if you do not need to add Runnable to the message queue
return;
}
ArchTaskExecutor's postToMainThread method is called if Runnable needs to be added to the message queue, which will be examined below
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
// setValue must be called on the main thread
@MainThread
protected void setValue(T value) {
// Check if it is in the main thread
assertMainThread("setValue");
/ / mVersion since
mVersion++;
// Assign value to mData
mData = value;
// Call the dispatchingValue method
dispatchingValue(null);
}
@SuppressWarnings("unchecked")
// The value is nullable
@Nullable
public T getValue(a) {
Object data = mData;
// Determine whether the value of mData is NOT_SET
if(data ! = NOT_SET) {// If the value of mData is not NOT_SET, this value is returned
return (T) data;
}
// Return null if the value of mData is NOT_SET
return null;
}
Copy the code
Let’s look at the postToMainThread method, which looks like this:
// ArchTaskExecutor.java
@NonNull
private TaskExecutor mDelegate;
private ArchTaskExecutor(a) {
// Create the DefaultTaskExecutor object
mDefaultTaskExecutor = new DefaultTaskExecutor();
// Assign mDefaultTaskExecutor to mDelegate
mDelegate = mDefaultTaskExecutor;
}
// A method to get an ArchTaskExecutor singleton, using Double Check Locking (DCL) to implement a singleton
@NonNull
public static ArchTaskExecutor getInstance(a) {
if(sInstance ! =null) {
return sInstance;
}
synchronized (ArchTaskExecutor.class) {
if (sInstance == null) {
// Call the ArchTaskExecutor constructor
sInstance = newArchTaskExecutor(); }}return sInstance;
}
@Override
public void postToMainThread(Runnable runnable) {
// Call the postToMainThread method of DefaultTaskExecutor
mDelegate.postToMainThread(runnable);
}
Copy the code
PostToMainThread ArchTaskExecutor inherits from TaskExecutor and is used to perform common tasks. The postToMainThread method calls the postToMainThread method of DefaultTaskExecutor.
// DefaultTaskExecutor.java
@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
// Create Handler, passing in Looper for the main threadmMainHandler = createAsync(Looper.getMainLooper()); }}}// Add runnable to the message queue. Runnable will be executed in the main thread
mMainHandler.post(runnable);
}
Copy the code
Then we look at the Runnable: mPostValueRunnable passed in as follows:
// LiveData.java
final Object mDataLock = new Object();
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run(a) {
Object newValue;
// Synchronization locks are applied to mDataLock objects
synchronized (mDataLock) {
// Assign mPendingData to newValue
newValue = mPendingData;
// set mPendingData to NOT_SET
mPendingData = NOT_SET;
}
// Call the setValue method, passing in newValuesetValue((T) newValue); }};Copy the code
PostValue finally calls setValue. If you call this method multiple times before the main thread executes the published task, only the last value will be distributed. Another thing to note is that if you call postValue first and pass in a, then setValue and pass in B, Then it will be set to B and then set to A.
The postValue method will call setValue again, and then dispatchingValue will call null, which we analyzed above, and just to be clear, if null is passed in, it will iterate through all the observers and distribute the content, Execute our logic by calling the Observer onChanged method.
digression
In the sample code, I use another Android Jetpack component, DataBinding, and call the setLifecycleOwner method of ViewDataBinding as follows:
// FirstFragment.kt
binding.lifecycleOwner = this
Copy the code
Let’s take a look at the corresponding source:
// ViewDataBinding.java
@MainThread
public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) {
// Omit some code
mLifecycleOwner = lifecycleOwner;
if(lifecycleOwner ! =null) {
if (mOnStartListener == null) {
mOnStartListener = new OnStartListener(this);
}
// Call the addObserver method
lifecycleOwner.getLifecycle().addObserver(mOnStartListener);
}
for(WeakListener<? > weakListener : mLocalFieldObservers) {if(weakListener ! =null) {
// Call the setLifecycleOwner method of WeakListenerweakListener.setLifecycleOwner(lifecycleOwner); }}}Copy the code
The code for the setLifecycleOwner method is as follows:
// ViewDataBinding.java
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
mObservable.setLifecycleOwner(lifecycleOwner);
}
Copy the code
LiveDataListener implements the Observer interface, so let’s look at the relevant code:
// ViewDataBinding.java
@Override
public void setLifecycleOwner(LifecycleOwner lifecycleOwner) { LifecycleOwner owner = (LifecycleOwner) lifecycleOwner; LiveData<? > liveData = mListener.getTarget();if(liveData ! =null) {
if(mLifecycleOwner ! =null) {
liveData.removeObserver(this);
}
if(lifecycleOwner ! =null) {
// Call LiveData's observe method
liveData.observe(owner, this);
}
}
mLifecycleOwner = owner;
}
// Based on the above analysis, the onChanged method is called when data changes
@Override
public void onChanged(@Nullable Object o) {
ViewDataBinding binder = mListener.getBinder();
if(binder ! =null) {
// Notify the corresponding UI update
binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0); }}Copy the code
The setLifecycleOwner method is used to setLifecycleOwner, which is used to observe changes to LiveData in the Binding. If LiveData is in one of the Binding expressions, but LifecycleOwner is not set, LiveData will not be observed, and updates to it will not update the UI.
In addition, I also used Kotlin coroutine, the code is as follows:
// MainViewModel.kt
fun changeSecondContent(text: String) =
viewModelScope.launch {
withContext(Dispatchers.Default) {
_secondContent.postValue(text)
}
}
Copy the code
The viewModelScope binds the coroutine scope to the ViewModel, which means that if the ViewModel is cleared, the coroutine scope is also cancelled. In the sample code, the MainViewModel life cycle is the same as the MainActivity life cycle. Prevent memory leaks when the coroutine performs the UI update logic after the Activity is destroyed and holds references to UI controls.
The withContext method is called and dispatchers. Default is passed, that is, the dispatchers. Default scheduler is used to call the specified suspend block, suspend until complete, and return the result. Where dispatchers. Default uses a shared background thread pool, that is to say, the logic of calling postValue method is executed in the worker thread, thus demonstrating that the worker thread sets the value of LiveData and updates the corresponding UI controls.
My GitHub: TanJiaJunBeyond
Common Android Framework: Common Android framework
My nuggets: Tan Jiajun
My simple book: Tan Jiajun
My CSDN: Tan Jiajun