Understand reactive programming and analyze LiveData deeply

Keywords: Java Fragment Android github

/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:

 

  1. Typically, you create a LiveData instance in the ViewModel to store some type of data.

  2. Create the Observer object and implement its onChanged method, which can control what to handle when the data stored in the LiveData object changes.

  3. 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:

 

  1. Avoid too large Activity or Fragment, because these interface controllers are only responsible for displaying data, not storing data status.

  2. 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

Talk about GC mechanism of Java

Do you know the difference between apply() and commit()?

176 original articles published, 54 praised, 200000 visitors+
Private letter follow

Posted by rowantrimmer on Sat, 18 Jan 2020 21:15:03 -0800