/Start/
This article mainly analyzes the source code of LiveData. This article uses the source code analysis of Android SDK 29.
/Definition/
LiveData is a kind of observable data storage class, which has life cycle awareness and follows the life cycle of application components (e.g. Activity, Fragment, Service (you can use LifecycleService, which is the Service that implements the LifecycleOwner interface)). This awareness ensures that LiveData only updates the application component observers in the active life cycle state.
If the life cycle of the observer (represented by the Observer class) is in the STARTED or restored state, LiveData will think that the observer is in the active state and notify it of the update, while the inactive observers who observe the LiveData object and register will not receive the change notification.
Application components all implement the Lifecycle owner interface. With this relationship, when the status of the corresponding Lifecycle object changes to deployed, these observers will be removed.
Advantages of using LiveData:
Make sure that the interface conforms to the data status
LiveData follows the Observer pattern. When the lifecycle state changes, it will notify the Observer object and then update the interface.
No memory leaks
When the lifecycle of an application component is in the deployed state, these observers are removed so that they no longer hold references to the application component.
No crash due to Activity stop
If the watcher's lifecycle is inactive (for example, returning activity on the stack), it will not accept any LiveData events.
No longer need to handle the lifecycle manually
The interface component only observes the relevant data, and will not stop or resume the observation. LiveData will automatically manage all these operations, because it can sense the relevant life cycle state changes when observing.
Data is always up to date
If the lifecycle becomes inactive, it will receive the latest data when it becomes active again. For example, an Activity that used to be 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, such as device rotation, it immediately receives the latest data.
shared resource
We can use the singleton pattern to inherit LiveData to encapsulate the relevant business logic so that they can be shared in the application.
/Using LiveData/
The operation steps are as follows:
-
Typically, you create a LiveData instance in the ViewModel to store some type of data.
-
Create the Observer object and implement its onChanged method, which can control what to handle when the data stored in the LiveData object changes.
-
In most cases, the LiveData object will be observed in the onCreate method of the application component, and the observer method of the LiveData object will be called. There are two parameters. The first parameter is the LifecycleOwner, which is an interface, which is implemented by Activity, Fragment and LifecycleService. The second parameter is the observer object created in the second step, so that the observer object can be customized Read the LiveData object to be notified of changes.
Note: make sure that the LiveData object used to update the interface is stored in the ViewModel object, not in the Activity or Fragment, for the following reasons:
-
Avoid too large Activity or Fragment, because these interface controllers are only responsible for displaying data, not storing data status.
-
Separating the LiveData instance from Activity and Fragment allows it to continue after configuration changes.
/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'
Because I used DataBinding here, I added the following code:
dataBinding { enabled = true }
The project structure is as follows:
Define MainViewModel and inherit ViewModel. Create two MutableLiveData in this class. firstContent is to demonstrate updating LiveData object in main thread, and secondContent is to demonstrate updating LiveData object in worker thread. The code is as follows:
/** * Created by TanJiaJun on 2019-12-22. */ class MainViewModel : ViewModel() { private val _firstContent = MutableLiveData<String>().apply { value = "First text" } val firstContent: LiveData<String> = _firstContent private val _secondContent = MutableLiveData<String>().apply { value = "Second text" } val secondContent: LiveData<String> = _secondContent // When the main thread updates the LiveData object, it calls the setValue method of MutableLiveData, which will be analyzed as follows fun changeFirstContent(text: String) { _firstContent.value = text } // When the worker thread updates the LiveData object, it calls the postValue method of MutableLiveData, which will be analyzed as follows fun changeSecondContent(text: String) = viewModelScope.launch { withContext(Dispatchers.Default) { _secondContent.postValue(text) } } }
Define the FirstFragment with the following code:
/** * 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 the first Button to call the changeFirstContent method in the MainViewModel, so that the text of the first TextView in the FirstFragment changes from the first text to the first text“ override fun onChangeFirstContentClick(view: View) { viewModel?.changeFirstContent(getString(R.string.first_content_changed)) } // Click the second Button to call the changeSecondContent method in the MainViewModel to change the text of the second TextView in the first fragment from the second text to the second text“ override fun onChangeSecondContentClick(view: View) { viewModel?.changeSecondContent(getString(R.string.second_content_changed)) } } interface SecondHandlers { fun onChangeFirstContentClick(view: View) fun onChangeSecondContentClick(view: View) }
Since I also use the DataBinding, ViewModel, and Kotlin processes, I will first analyze the source code according to the official document writing method.
/Source code analysis/
LiveData is an abstract class. We mainly use MutableLiveData and MediatorLiveData. Let's look at the structure chart of LiveData
Let's look at the following common methods. First, we look at the observer method. The observer method is used to add the specified observer to the observation list in the specified lifecycle owner. The code is as follows:
// LiveData.java // Create a Map with key as Observer and value as Observer wrapper private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>(); @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { // Check whether it is in the main thread, and throw an exception if it is not assertMainThread("observe"); // Determine whether the lifecycle owner is in the planned state if (owner.getLifecycle().getCurrentState() == DESTROYED) { // If it is return, it will not be distributed return; } // Create LifecycleBoundObserver to wrap the incoming LifecycleOwner and Observer. This class will be analyzed below LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); // Store the LifecycleBoundObserver to the SafeIterableMap. The key is Observer and the value is Observer wrapper. If the Observer is stored for the first time, null will be returned ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); // The first condition is to determine whether to store the Observer for the first time. The second condition is to determine whether the Observer has been attached to the lifecycle owner if (existing != null && !existing.isAttachedTo(owner)) { // If the Observer is not stored for the first time and has been attached to the lifecycle owner, throw "cannot add the Observer attached to different lifecycles, but it is the same Observer throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } // Judge again whether to store the Observer for the first time if (existing != null) { // If it is not the first time to store this Observer, return return; } // Call addObserver method of Lifecycle owner.getLifecycle().addObserver(wrapper); }
Safeiterable Map is a Map, which is actually a LinkedList and supports modification during iteration, but it is not thread safe. LiveData uses it to store lifecycle bundobserver.
Lifecycle is an abstract class. Its only subclass is lifecycle registry. Let's look at its addObserver method. The code is as follows:
// LifecycleRegistry.java // Create fastsafeiterable map with key as lifecycle observer and value as observer with state private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap = new FastSafeIterableMap<>(); // The provider with this Lifecycle only keeps weak references on the Lifecycle owner, so if someone divulges the Lifecycle objects, they will not divulge the whole Activity or Fragment, but doing so will also divulge all Listener objects, because strong references are kept 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; // Package lifecycle observer (we passed in lifecycle bundobserver) and State as observer with State ObserverWithState statefulObserver = new ObserverWithState(observer, initialState); // Store the ObserverWithState in fastsafeiterable map, key is lifecycle bundobserver, and value is ObserverWithState. If this is the first time to store the ObserverWithState, null will be returned ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver); // Determine whether to store the observer with state for the first time if (previous != null) { // If it is not the first time to store this ObserverWithState, return return; } LifecycleOwner lifecycleOwner = mLifecycleOwner.get(); // Determine if lifecycle owner is null if (lifecycleOwner == null) { // If it is null, it should be destroyed immediately and quickly returned return; } boolean isReentrance = 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); } // Determine if it's at the top of the stack if (!isReentrance) { // Perform synchronization if it is at the top of the stack sync(); } mAddingObserverCounter--; }
If it's at the top of the stack, the sync method will be called, so it won't be called at the time of reentry. Let's take a look at this method. The code is as follows:
// LifecycleRegistry.java private void sync() { LifecycleOwner lifecycleOwner = mLifecycleOwner.get(); // Determine if lifecycle owner is null if (lifecycleOwner == null) { // If it is null, throw an exception to prove that the lifecycle owner of the lifecycle registry has been garbage collected. 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; // You don't need to check whether the eldest is null, because isSynced has helped us judge it if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) { // If the state of ObserverWithState is smaller than the current state, the observer will be notified of the change of state through reverse traversal 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 observer will be notified of the change of state by positive sequence traversal forwardPass(lifecycleOwner); } } mNewEventOccurred = false; }
Let's look at the forwardPass method and backwardPass method. The code is as follows:
// LifecycleRegistry.java // Positive order traversal changes state and informs observer private void forwardPass(LifecycleOwner lifecycleOwner) { Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator = mObserverMap.iteratorWithAdditions(); // Positive order traversal mbobservermap 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 of ObserverWithState observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState)); popParentState(); } } } // Reverse traversal changes state and informs observer private void backwardPass(LifecycleOwner lifecycleOwner) { Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator = mObserverMap.descendingIterator(); // Traversing mbobservermap in reverse order 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 of ObserverWithState observer.dispatchEvent(lifecycleOwner, event); popParentState(); } } }
ObserverWithState is a static internal class of LifecycleRegistry. Let's look at the dispatchEvent method of ObserverWithState. The code is as follows:
// LifecycleRegistry.java static class ObserverWithState { State mState; LifecycleEventObserver mLifecycleObserver; ObserverWithState(LifecycleObserver observer, State initialState) { // Mlifcycleobserver is LifecycleBoundObserver mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer); mState = initialState; } void dispatchEvent(LifecycleOwner owner, Event event) { State newState = getStateAfter(event); mState = min(mState, newState); // Call the onStateChanged method of LifecycleBoundObserver mLifecycleObserver.onStateChanged(owner, event); mState = newState; } }
LifecycleBoundObserver is an internal class of LiveData. Let's see the onStateChanged method. The code is as follows:
// LiveData.java // This method determines whether the value of Lifecycle status is greater than or equal to the value of STARTED status, so as to determine whether it is an active status @Override boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { // Determine whether Lifecycle is in the planned state if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { // If the Lifecycle is in the deployed state, remove the observer and return, the removeObserver method will be analyzed as follows removeObserver(mObserver); return; } // If Lifecycle is not a deployed state, call the activeStateChanged method and pass in the Boolean value from the shouldBeActive method activeStateChanged(shouldBeActive()); }
State is an enumeration, the code is as follows:
@SuppressWarnings("WeakerAccess") public enum State { DESTROYED, INITIALIZED, CREATED, STARTED, RESUMED; // Determine whether the value of the current state is greater than or equal to the value of the incoming state public boolean isAtLeast(@NonNull State state) { return compareTo(state) >= 0; } }
We can see that the only values greater than or equal to STARTED are STARTED and RESUMED, that is to say, this method is to determine whether it is active.
The activeStateChanged method code is as follows:
// LiveData.java void activeStateChanged(boolean newActive) { // Judge whether the status changes if (newActive == mActive) { // return if there is no change return; } // If there is a change, set the status immediately so that no content is sent to the inactive observer mActive = newActive; boolean wasInactive = LiveData.this.mActiveCount == 0; // If the current state is active, 1 will be added, otherwise 1 will be decreased LiveData.this.mActiveCount += mActive ? 1 : -1; if (wasInactive && mActive) { // If the mcactivecount changes from 0 to 1 and the current state is active, the onActive method is called onActive(); } if (LiveData.this.mActiveCount == 0 && !mActive) { // If the mcactivecount changes from 1 to 0 and the current state is inactive, onInactive will be called onInactive(); } if (mActive) { // If the current state is active, the dispatchingValue is called, and the lifecycle bundobserver is passed in dispatchingValue(this); } }
Let's look at the dispatchingValue method. The code is as follows:
// LiveData.java @SuppressWarnings("WeakerAccess") /* synthetic access */ void dispatchingValue(@Nullable ObserverWrapper initiator) { // Determine if distribution is in progress if (mDispatchingValue) { // If distribution is in progress, set mdispatchinvalid to true, which means that distribution is invalid, and return mDispatchInvalidated = true; return; } // Distributing mDispatchingValue = true; do { mDispatchInvalidated = false; // Judge whether the initiator is null. According to the above code, it is not null here. Pay attention to the logic of null. When we talk about setValue, postValue and getValue, we will talk about if (initiator != null) { // If the initiator is not null, the considerNotify method will be called, and the lifecycle bundobserver will be passed in considerNotify(initiator); initiator = null; } else { // If the initiator is null, it will traverse the mbobservers for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { // Call the considerNotify method considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); // Distribution is over mDispatchingValue = false; }
The two variables, mDispatchingValue and mDispatchInvalidated, are used to prevent duplicate distribution of the same content.
The considerNotify method code is as follows:
// LiveData.java @SuppressWarnings("unchecked") private void considerNotify(ObserverWrapper observer) { // Judge whether it is active if (!observer.mActive) { // return if not active return; } // Check whether the latest status is active before distribution to prevent the changed status, but we haven't received the event if (!observer.shouldBeActive()) { // If it's not active, notify it not to distribute observer.activeStateChanged(false); return; } // Determine whether it is the latest data if (observer.mLastVersion >= mVersion) { // If it is the latest data, it will not be distributed return; } // Set version to the latest version observer.mLastVersion = mVersion; // Call onChanged method observer.mObserver.onChanged((T) mData); }
The code of onChanged method is as follows:
// Observer.java public interface Observer<T> { void onChanged(T t); }
onChanged method is the method we need to implement. We can implement the logic to be executed when the data changes in this method.
Do you remember sync? If you forget, you can look up again. This sync method will also be called by moveToState method. The code is as follows:
// LifecycleRegistry.java private void moveToState(State next) { if (mState == next) { return; } mState = next; // Determine if you are synchronizing or adding observers if (mHandlingEvent || mAddingObserverCounter != 0) { mNewEventOccurred = true; // we will figure out what to do on upper level. // return if you are synchronizing or adding observers return; } mHandlingEvent = true; // Call sync method sync(); mHandlingEvent = false; }
The moveToState method will also be called by the handlelifcycleevent method. The function of this method is to change the life cycle state and inform the observer. If the current life cycle state is the same as that of the last call to this method, the call to this method has no effect. The code is as follows:
// LifecycleRegistry.java public void handleLifecycleEvent(@NonNull Lifecycle.Event event) { State next = getStateAfter(event); moveToState(next); }
When was the lifecycle registry object created? What's the point? In our example code, MainActivity inherits AppCompatActivity, which inherits FragmentActivity, and FragmentActivity inherits androidx.activity.ComponentActivity. Let's look at the code of androidx.activity.ComponentActivity:
// androidx.activity.ComponentActivity.java // Create a lifecycle registry object private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mSavedStateRegistryController.performRestore(savedInstanceState); // Inject ReportFragment ReportFragment.injectIfNeededIn(this); if (mContentLayoutId != 0) { setContentView(mContentLayoutId); } }
Let's take a look at the code of ReportFragment. The code is 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 the FragmentActivity of support lib, so use the FragmentActivity of 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); // Calling the dispatch method, passing lifecycle.event.on "create dispatch(Lifecycle.Event.ON_CREATE); } @Override public void onStart() { super.onStart(); dispatchStart(mProcessListener); // Call the dispatch method and pass in lifecycle.event.on "start dispatch(Lifecycle.Event.ON_START); } @Override public void onResume() { super.onResume(); dispatchResume(mProcessListener); // Call the dispatch method and pass lifecycle.event.on "resume dispatch(Lifecycle.Event.ON_RESUME); } @Override public void onPause() { super.onPause(); // Call the dispatch method and pass in lifecycle.event.on "pause dispatch(Lifecycle.Event.ON_PAUSE); } @Override public void onStop() { super.onStop(); // Call the dispatch method and pass in lifecycle.event.on'stop dispatch(Lifecycle.Event.ON_STOP); } @Override public void onDestroy() { super.onDestroy(); // Call the dispatch method and pass in lifecycle. Event. On? Deploy dispatch(Lifecycle.Event.ON_DESTROY); // Ensure that the reference of an Activity is not disclosed mProcessListener = null; } private void dispatch(Lifecycle.Event event) { Activity activity = getActivity(); // Determine whether the Activity implements the LifecycleRegistryOwner interface if (activity instanceof LifecycleRegistryOwner) { // Call handlelevelcycleevent method ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event); return; } // Determine whether the Activity implements the lifecycle owner interface if (activity instanceof LifecycleOwner) { Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle(); // Determine if Lifecycle is a subclass of Lifecycle registry if (lifecycle instanceof LifecycleRegistry) { // Call the handleLifecycleEvent method ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event); } } }
ReportFragment is used to sense the life cycle of an Activity. Whenever the life cycle of an Activity changes, the handlelifcycleevent method of LifecycleRegistry will be called, then the moveToState method will be called, and finally the sync method will be called, so that LiveData can sense the life cycle of an Activity. In fact, this is another very important part of Android Jetpack The principle of the important component Lifecycle is also shown in the Fragment code:
// Fragment.java LifecycleRegistry mLifecycleRegistry; @Nullable FragmentViewLifecycleOwner mViewLifecycleOwner; MutableLiveData<LifecycleOwner> mViewLifecycleOwnerLiveData = new MutableLiveData<>(); private void initLifecycle() { // Create a lifecycle registry object mLifecycleRegistry = new LifecycleRegistry(this); mSavedStateRegistryController = SavedStateRegistryController.create(this); // Determine whether the current Android version is greater than 4.4 if (Build.VERSION.SDK_INT >= 19) { mLifecycleRegistry.addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { // Determine whether the current event is on? Stop if (event == Lifecycle.Event.ON_STOP) { // Judge whether View is null if (mView != null) { // If not empty, cancel any delayed high-level input events previously published to the event queue mView.cancelPendingInputEvents(); } } } }); } } void performCreate(Bundle savedInstanceState) { // Omit some codes // Call handlelifcycleevent and pass 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 lifecycle owner of the Fragment's View mViewLifecycleOwner = new FragmentViewLifecycleOwner(); mView = onCreateView(inflater, container, savedInstanceState); // Judge whether View is null if (mView != null) { // Create the Lifecycle of the Fragment's View mViewLifecycleOwner.initialize(); // Then notify some observers of the new lifecycle owner mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner); } else { // Determine whether the fragmentviewlifecycle owner is initialized if (mViewLifecycleOwner.isInitialized()) { // If View is null and FragmentViewLifecycleOwner is initialized, an exception is thrown throw new IllegalStateException("Called getViewLifecycleOwner() but " + "onCreateView() returned null"); } mViewLifecycleOwner = null; } } void performStart() { // Omit some codes // Call onStart method onStart(); // Omit some codes // Call the handlelifcycleevent method of LifecycleRegistry and pass in lifecycle.event.on'start mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START); // Judge whether View is null if (mView != null) { // If View is not null, call handlelifcycleevent of FragmentViewLifecycleOwner and pass in lifecycle.event.on "start mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_START); } // Omit some codes } void performResume() { // Omit some codes // Call the onResume method onResume(); // Omit some codes // Call the handlelifcycleevent method of LifecycleRegistry and pass in lifecycle.event.on "resume mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME); // Judge whether View is null if (mView != null) { // If View is not null, call handlelifcycleevent of FragmentViewLifecycleOwner and pass in lifecycle.event.on "resume mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_RESUME); } // Omit some codes } void performPause() { // Omit some codes // Judge whether View is null if (mView != null) { // If the View is not null, call the handlelifcycleevent method of the FragmentViewLifecycleOwner and pass in lifecycle.event.on "pause mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE); } // Call the handlelifcycleevent method of LifecycleRegistry and pass in lifecycle.event.on "pause mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE); // Omit some codes // Call onPause method onPause(); // Omit some codes } void performStop() { // Omit some codes // Judge whether View is null if (mView != null) { // If the View is not null, call the handlelifcycleevent method of the FragmentViewLifecycleOwner and pass in lifecycle.event.on ﹣ stop mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_STOP); } // Call the handlelifcycleevent method of LifecycleRegistry and pass in lifecycle.event.on ﹣ stop mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP); // Omit some codes // Call onStop method onStop(); // Omit some codes } void performDestroyView() { // Judge whether View is null if (mView != null) { // If the View is not null, call the handlelifcycleevent method of the FragmentViewLifecycleOwner and pass in lifecycle.event.on "destroy mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); } // Omit some codes onDestroyView(); // Omit some codes } void performDestroy() { // Omit some codes // Call the handlelifcycleevent method of LifecycleRegistry, and pass in lifecycle.event.on'destroy mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); // Omit some codes // Call the onDestroy method onDestroy(); // Omit some codes }
FragmentViewLifecycleOwner represents the life cycle of Fragment View. In most cases, it reflects the life cycle of Fragment itself, but the life cycle of Fragment is certainly longer than that of its View.
Similarly, whenever the lifecycle of a Fragment changes, the handlelifcycleevent method of the lifecycle registry and the handlelifcycleevent method of the fragmentviewlifecycle owner will be called, and then the moveToState method will be called, and finally the sync method will be called, so that LiveData can sense the lifecycle of the Fragment.
Then let's look at the removeObserver method. The code is as follows:
// LiveData.java // This method is called in the main thread @MainThread public void removeObserver(@NonNull final Observer<? super T> observer) { // Determine whether it is in the main thread assertMainThread("removeObserver"); ObserverWrapper removed = mObservers.remove(observer); if (removed == null) { // return if it is null return; } // Calling the detachObserver method of LifecycleBoundObserver removed.detachObserver(); // Call the activeStateChanged method of LifecycleBoundObserver, and pass in false. This method has been analyzed above, and will not be repeated here removed.activeStateChanged(false); }
The code of detachObserver method is as follows:
// LiveData.java @Override void detachObserver() { // Call the removeObserver method of lifecycle registry mOwner.getLifecycle().removeObserver(this); }
The code of the removeObserver method is as follows:
// LifecycleRegistry.java @Override public void removeObserver(@NonNull LifecycleObserver observer) { // Remove this observer mObserverMap.remove(observer); }
According to the above code, the system will also call this method to remove the observer and prevent memory leakage.
That's how LiveData perceives life cycles. Another method is also commonly used: the observeForever method. Let's look at its code:
// LiveData.java @MainThread public void observeForever(@NonNull Observer<? super T> observer) { assertMainThread("observeForever"); // Create alwaysactivetobserver AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer); ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing instanceof LiveData.LifecycleBoundObserver) { // If existing is an instance of LiveData.LifecycleBoundObserver class, throw an exception throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } if (existing != null) { return; } // Call the activeStateChanged method of alwaysactivetobserver and pass in true wrapper.activeStateChanged(true); }
Look at the code of alwaysactivetobserver:
// LiveData.java private class AlwaysActiveObserver extends ObserverWrapper { AlwaysActiveObserver(Observer<? super T> observer) { super(observer); } // It rewrites the shouldBeActive method and returns true. According to the above code analysis, this method is used to determine whether it is active. Here, it always returns true, that is to say, it is always active @Override boolean shouldBeActive() { return true; } }
observeForever is used to add the specified observer to the observation list, similar to calling the observer method, but the given lifecycle owner state is always active, which means that the observer will always receive all events, so if you want to stop observing the LiveData, you need to manually call the removeObserver method.
Then let's look at the source code of the other three important methods: setValue method, postValue method and getValue method. SetValue method and postValue method are used to set values for LiveData, and getValue method is used to take values from LiveData. The code is as follows:
// LiveData.java final Object mDataLock = new Object(); protected void postValue(T value) { boolean postTask; // Synchronization lock acts on mDataLock object synchronized (mDataLock) { // postTask uses whether the value of mPendingData is not set to determine whether it is necessary to add Runnable to the message queue postTask = mPendingData == NOT_SET; // Assign value to mPendingData mPendingData = value; } // Determine if 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; } // If you need to add Runnable to the message queue, call the postmainthread method of ArchTaskExecutor, which will be analyzed as follows ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); } // setValue must be called on the main thread @MainThread protected void setValue(T value) { // Check whether it is in the main thread assertMainThread("setValue"); // mVersion auto increment mVersion++; // Assign value to mData mData = value; // Call the dispatchingValue method dispatchingValue(null); } @SuppressWarnings("unchecked") // Objects with nullable values @Nullable public T getValue() { Object data = mData; // Determine whether the value of mData is not set if (data != NOT_SET) { // This value is returned if the value of mData is not not not set return (T) data; } // Returns null if the value of mData is not set return null; }
Let's look at the posttomaintenthread method. The code is as follows:
// ArchTaskExecutor.java @NonNull private TaskExecutor mDelegate; private ArchTaskExecutor() { // Create DefaultTaskExecutor object mDefaultTaskExecutor = new DefaultTaskExecutor(); // Assign mdefaulttaxecutor to mDelegate mDelegate = mDefaultTaskExecutor; } // The method of obtaining a single instance of ArchTaskExecutor, using Double Check Locking (DCL) to implement a single instance @NonNull public static ArchTaskExecutor getInstance() { if (sInstance != null) { return sInstance; } synchronized (ArchTaskExecutor.class) { if (sInstance == null) { // Construction method of calling ArchTaskExecutor sInstance = new ArchTaskExecutor(); } } return sInstance; } @Override public void postToMainThread(Runnable runnable) { // Call the posttomamainthread method of DefaultTaskExecutor mDelegate.postToMainThread(runnable); }
ArchTaskExecutor inherits TaskExecutor and is used to execute some public tasks. We see that the posttomaintenthread method will call the posttomaintenthread method of DefaultTaskExecutor. The code is as follows:
// DefaultTaskExecutor.java @Override public void postToMainThread(Runnable runnable) { if (mMainHandler == null) { synchronized (mLock) { if (mMainHandler == null) { // Create Handler, passing in Looper of main thread mMainHandler = createAsync(Looper.getMainLooper()); } } } // Add runnable to the message queue and runnable will execute in the main thread mMainHandler.post(runnable); }
Then let's look at the passed in Runnable: mPostValueRunnable. The code is as follows:
// LiveData.java final Object mDataLock = new Object(); private final Runnable mPostValueRunnable = new Runnable() { @SuppressWarnings("unchecked") @Override public void run() { Object newValue; // Synchronization lock acts on mDataLock object synchronized (mDataLock) { // Assign mPendingData to newValue newValue = mPendingData; // Set mPendingData to not set mPendingData = NOT_SET; } // Call setValue method, pass in newValue setValue((T) newValue); } };
The postValue method will call the setValue method in the end. If the method is invoked many times before the main thread executes the published task, it will only distribute the last value. There is another point to note that it is assumed that the postValue method is first invoked, and the A is introduced, then the setValue method is invoked, and the b is passed in, then it is set to b and then set to a.
The postValue method will call the setValue method finally, and then the dispatchingValue method will be called, and null will be passed in. This method has also been analyzed above. Here I will roughly say that if NULL is passed in, all observers will be traversed, and then the content will be distributed, and the onChanged method of Observer will be called to execute our logic.
/Digression/
In the example code, I use another component of Android Jetpack: DataBinding, and call the setLifecycleOwner method of ViewDataBinding. The code is as follows:
// FirstFragment.kt binding.lifecycleOwner = this
Let's look at the corresponding source code:
// ViewDataBinding.java @MainThread public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) { // Omit some codes mLifecycleOwner = lifecycleOwner; if (lifecycleOwner != null) { if (mOnStartListener == null) { mOnStartListener = new OnStartListener(this); } // Call addObserver method lifecycleOwner.getLifecycle().addObserver(mOnStartListener); } for (WeakListener<?> weakListener : mLocalFieldObservers) { if (weakListener != null) { // Call the setLifecycleOwner method of WeakListener weakListener.setLifecycleOwner(lifecycleOwner); } } }
The code of the setlifecycle owner method is as follows:
// ViewDataBinding.java public void setLifecycleOwner(LifecycleOwner lifecycleOwner) { mObservable.setLifecycleOwner(lifecycleOwner); }
Here, the Mo observable is a LiveDataListener, and the LiveDataListener also implements the Observer interface, so let's look at the relevant code, which is as follows:
// 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 the observe method of LiveData liveData.observe(owner, this); } } mLifecycleOwner = owner; } // According to the above analysis, when the data changes, the onChanged method will be called @Override public void onChanged(@Nullable Object o) { ViewDataBinding binder = mListener.getBinder(); if (binder != null) { // Notify corresponding UI updates binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0); } }
The setLifecycleOwner method is used to set up LifecycleOwner to observe the change of LiveData in the Binding. If LiveData is in one of the Binding expressions, but LifecycleOwner is not set, then LiveData will not be observed, and the update to it will not update the UI.
In addition, I also used the Kotlin process. The code is as follows:
// MainViewModel.kt fun changeSecondContent(text: String) = viewModelScope.launch { withContext(Dispatchers.Default) { _secondContent.postValue(text) } }
viewModelScope is to bind the scope of the collaboration to ViewModel, that is to say, if ViewModel is cleared, the scope of the collaboration will also be cancelled. In the example code, the life cycle of MainViewModel and MainActivity will be consistent, so as to prevent the collaboration from executing the logic of updating UI after the Activity is destroyed, and also holding the reference of UI control, resulting in memory leakage.
Calling the withContext method and passing in Dispatchers.Default, that is to say, using Dispatchers.Default scheduler to call the specified suspend block, suspend until completion, and return the result, in which Dispatchers.Default uses the shared background thread pool, that is to say, calling the postValue method is executed in the working thread, so as to demonstrate that in the working thread Set the value of LiveData and update the corresponding UI control.
My GitHub: TanJiaJunBeyond address is as follows:
https://github.com/TanJiaJunBeyond
Android universal framework: the address of Android universal framework (kotlin MVVM) is as follows:
https://github.com/TanJiaJunBeyond/AndroidGenericFramework
Recommended reading:
A time-consuming way to quickly locate your Android App