Tips: This article is based on the Lifecycle 2.4.0 analysis. The latest release of the Lifecycle component is 2.4.0 as of completion.

Version 2.4.0 Changed

  • abandoned@OnLifecycleEvent. Should be to switch toLifecycleEventObserverDefaultLifecycleObserver
  • toandroidx.lifecycle:lifecycle-runtime-ktxAdded new coroutine API:
    • Lifecycle.repeatOnLifecycle– This API executes a block of code in a coroutine when Lifecycle is in at least one state. Lifecycle will cancel and restart when Lifecycle enters and exits the target state;
    • Flow.flowWithLifecycle– This API emits values from the upstream flow when Lifecycle is at least in a state.
  • Now,lifecycle-processuseandroidx.startupTo initialize theProcessLifecycleOwner. Previously this operation was performed byandroidx.lifecycle.ProcessLifecycleOwnerInitializerThe finished.
    dependencies {
        def lifecycle_version = "2.4.0"
        def arch_version = "2.1.0."

        // ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
        // ViewModel utilities for Compose
        implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
        // LiveData
        implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
        // Lifecycles only (without ViewModel or LiveData)
        implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

        // Saved state module for ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"

        // Annotation processor
        kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
        // alternately - if using Java8, use the following instead of lifecycle-compiler
        implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

        // optional - helpers for implementing LifecycleOwner in a Service
        implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"

        // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
        implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

        // optional - ReactiveStreams support for LiveData
        implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version"

        // optional - Test helpers for LiveData
        testImplementation "androidx.arch.core:core-testing:$arch_version"
    }
Copy the code

Today we focus to see androidx. Lifecycle: lifecycle – runtime – KTX: $lifecycle_version in content.

LifecycleOwner LifecycleObserver and Lifecycle

Lifecyle components have three cornerstones: Lifecycle, LifecycleObserver and LifecycleOwner. LifecycleObserver, as the name suggests, stands for the observer in the lifecycle:

    public interface LifecycleObserver {}Copy the code

LifecycleOwner is the owner of the lifecycle. In Android, both Fragment and ComponentActivity implement this interface:

    public interface LifecycleOwner {
        @NonNull
        Lifecycle getLifecycle(a);
    }
Copy the code

Finally Lifecycle, every LifecycleOwner will hold a Lifecycle object that will allow us to obtain the current Lifecycle and add and remove observers:

    public abstract class Lifecycle {
        public abstract void addObserver(@NonNull LifecycleObserver observer);

        public abstract void removeObserver(@NonNull LifecycleObserver observer);

        public abstract State getCurrentState(a);
    }
Copy the code

Lifecycle also defines two enumerated classes Event and State:

    public enum Event {
        ON_CREATE,      
        ON_START,       
        ON_RESUME,       
        ON_PAUSE,       
        ON_STOP,       
        ON_DESTROY,       
        ON_ANY;
    }

    public enum State {
        DESTROYED,
        INITIALIZED,
        CREATED,
        STARTED,
        RESUMED;
    }
Copy the code

The upFrom(), upTo(), downFrom(), downTo(), and getTargetState() methods are provided in the Event class, Used to obtain the events corresponding to the forward and backward conversion of the current State and the State corresponding to the current Event respectively:

    @Nullable
    public static Event upFrom(@NonNull State state) {
        switch (state) {
            case INITIALIZED:
                return ON_CREATE;
            case CREATED:
                return ON_START;
            case STARTED:
                return ON_RESUME;
            default:
                return null; }}@Nullable
    public static Event upTo(@NonNull State state) {
        switch (state) {
            case CREATED:
                return ON_CREATE;
            case STARTED:
                return ON_START;
            case RESUMED:
                return ON_RESUME;
            default:
                return null; }}@NonNull
    public State getTargetState(a) {
        switch (this) {
            case ON_CREATE:
            case ON_STOP:
                return State.CREATED;
            case ON_START:
            case ON_PAUSE:
                return State.STARTED;
            case ON_RESUME:
                return State.RESUMED;
            case ON_DESTROY:
                return State.DESTROYED;
            case ON_ANY:
                break;
        }
        throw new IllegalArgumentException(this + " has no target state");
    }
Copy the code

The isAtLeast() method is provided in the State class to determine whether the current State is higher than a given minimum State:

    public boolean isAtLeast(@NonNull State state) {
        return compareTo(state) >= 0;
    }
Copy the code

The overall relationship between State and Event is shown in the figure:

Let’s look at the Fragment and ComponentActivity implementation getLifecycle() :

    LifecycleRegistry mLifecycleRegistry;

    @NonNull
    @Override
    public Lifecycle getLifecycle(a) {
        return mLifecycleRegistry;
    }
Copy the code

LifecycleRegistry is the Lifecycle implementation class and there is a bit of code to note:

    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        enforceMainThreadIfNeeded("addObserver");
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        / / look here
        ObserverWithState statefulObserver = newObserverWithState(observer, initialState); ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver); ./ / look hereState targetState = calculateTargetState(observer); mAddingObserverCounter++; .while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            pushParentState(statefulObserver.mState);
            final Event event = Event.upFrom(statefulObserver.mState);
            if (event == null) {
                throw new IllegalStateException("no event up from " + statefulObserver.mState);
            }
            statefulObserver.dispatchEvent(lifecycleOwner, event);
            popParentState();
            // mState / subling may have been changed recalculatetargetState = calculateTargetState(observer); }... }Copy the code
  • This code shows that even if we add a subscriber when State is RESUMED, that subscriber will receive a full stream of ON_CREATE, ON_START, and ON_RESUME events.

Lifecycle can be used with the OnLifecycleEvent annotation prior to 2.4.0:

    public class MLifecycleObserver implements LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        public void onCreate(a) { Log.d(TAG, "onCreate"); }}Copy the code

For Java8, it can be used by implementing DefaultLifecycleObserver. After 2.4.0, OnLifecycleEvent has been deprecated and needs to be replaced with DefaultLifecycleObserver or LifecycleEventObserver:

If both DefaultLifecycleObserver and LifecycleEventObserver are implemented, DefaultLifecycleObserver will be triggered first. LifecycleEventObserver is then triggered.

Everything is ready except the east wind. How does Lifecyle bind to components to capture life cycle changes? Let’s look at the implementation of ComponentActivity:

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ReportFragment.injectIfNeededIn(this);
    }
Copy the code

ReportFragment. InjectIfNeededIn (this) is the key to:

public static void injectIfNeededIn(Activity activity) {
        if (Build.VERSION.SDK_INT >= 29) {
            // On API 29+, we can register for the correct Lifecycle callbacks directly
            LifecycleCallbacks.registerIn(activity);
        }
        // Prior to API 29 and to maintain compatibility with older versions of
        // ProcessLifecycleOwner (which may not be updated when lifecycle-runtime is updated and
        // need to support activities that don't extend from FragmentActivity from support lib),
        // use a framework fragment to get the correct timing of Lifecycle events
        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(); }}Copy the code

repeatOnLifecycle & flowWithLifecycle

FlowWithLifecycle is essentially calling repeatOnLifecycle:

    public fun <T> Flow<T>.flowWithLifecycle(
        lifecycle: Lifecycle,
        minActiveState: Lifecycle.State = Lifecycle.State.STARTED
    ): Flow<T> = callbackFlow {
        lifecycle.repeatOnLifecycle(minActiveState) {
            this@flowWithLifecycle.collect {
                send(it)
            }
        }
        close()
    }
Copy the code

RepeatOnLifecycle implementation is as follows:

    public suspend fun Lifecycle.repeatOnLifecycle(
        state: Lifecycle.State,
        block: suspend CoroutineScope. () - >Unit
    ){ require(state ! == Lifecycle.State.INITIALIZED) {"repeatOnLifecycle cannot start work with the INITIALIZED lifecycle state."
        }

        if (currentState === Lifecycle.State.DESTROYED) {
            return
        }

        // This scope is required to preserve context before we move to Dispatchers.Main
        coroutineScope {
            withContext(Dispatchers.Main.immediate) {
                // Check the current state of the lifecycle as the previous check is not guaranteed
                // to be done on the main thread.
                if (currentState === Lifecycle.State.DESTROYED) return@withContext

                // Instance of the running repeating coroutine
                var launchedJob: Job? = null

                // Registered observer
                var observer: LifecycleEventObserver? = null
                try {
                    // Suspend the coroutine until the lifecycle is destroyed or
                    // the coroutine is cancelled
                    suspendCancellableCoroutine<Unit> { cont ->
                        // Lifecycle observers that executes `block` when the lifecycle reaches certain state, and
                        // cancels when it falls below that state.
                        val startWorkEvent = Lifecycle.Event.upTo(state)
                        val cancelWorkEvent = Lifecycle.Event.downFrom(state)
                        val mutex = Mutex()
                        observer = LifecycleEventObserver { _, event ->
                            if (event == startWorkEvent) {
                                // Launch the repeating work preserving the calling context
                                launchedJob = this@coroutineScope.launch {
                                    // Mutex makes invocations run serially,
                                    // coroutineScope ensures all child coroutines finish
                                    mutex.withLock {
                                        coroutineScope {
                                            block()
                                        }
                                    }
                                }
                                return@LifecycleEventObserver
                            }
                            if(event == cancelWorkEvent) { launchedJob? .cancel() launchedJob =null
                            }
                            if (event == Lifecycle.Event.ON_DESTROY) {
                                cont.resume(Unit)}}this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)
                    }
                } finally{ launchedJob? .cancel() observer? .let {this@repeatOnLifecycle.removeObserver(it)
                    }
                }
            }
        }
    }
Copy the code

ProcessLifecycleOwner

Lifecycle also provides a ProcessLifecycleOwner to capture the entire Lifecycle of the application, where ON_CREATE occurs only once at creation time, And the ON_DESTROY event never occurs. ON_START, ON_RESUME will be sent when the first activity is triggered; ON_PAUSE and ON_STOP are emitted after a short delay after the last activity is triggered. And you can think about why? This is simply to avoid sending the wrong Event to destroy the rebuild caused by configerationChanged. Then you can do this again when you need to monitor the scenes before and after the application:

    ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver {
        override fun onStart(owner: LifecycleOwner) {
            super.onStart(owner)
            Log.e(TAG,"Come to the front")}override fun onStop(owner: LifecycleOwner) {
            super.onStop(owner)
            Log.e(TAG,"Go backstage")}})Copy the code