After we finish ActivityStack, when the TaskRecord moves to the front end, and add the ActivityRecord to the TaskRecord. Our data structure is processed, but we still need to start the Activity across the process. But, of course, starting an Activity can’t happen out of thin air. To write our own logic, it’s easy to think of the following rules:

  • 1. Identify the Activity you are currently interacting with
  • 2. Check whether the process corresponding to the Activity exists
  • 3. After the above two steps are complete, start the Activity across the process.

ResumeTopActivityInnerLocked start-up Activity

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { ... final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); final boolean hasRunningActivity = next ! = null; // TODO: Maybe this entire condition can get removed? if (hasRunningActivity && ! isAttached()) { return false; }... boolean lastResumedCanPip = false; ActivityRecord lastResumed = null; final ActivityStack lastFocusedStack = mStackSupervisor.getLastStack(); . final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) ! = 0 &&! lastResumedCanPip; / / stop the Task Activity within the stack of the Boolean pausing. = mStackSupervisor pauseBackStacks (userLeaving, next, false); if (mResumedActivity ! = null) { pausing |= startPausingLocked(userLeaving, false, next, false); } // Update the lRU data if (pausing &&! resumeWhilePausing) { ... if (next.app ! = null && next.app.thread ! = null) { mService.updateLruProcessLocked(next.app, true, null); } if (lastResumed ! = null) { lastResumed.setWillCloseOrEnterPip(true); } return true; } else if (mResumedActivity == next && next.isState(RESUMED) && mStackSupervisor.allResumedActivitiesComplete()) { .....  return true; }... ActivityStack lastStack = mStackSupervisor.getLastStack(); if (next.app ! = null && next.app.thread ! = null) { final boolean lastActivityTranslucent = lastStack ! = null && (lastStack.inMultiWindowMode() || (lastStack.mLastPausedActivity ! = null && ! lastStack.mLastPausedActivity.fullscreen)); / / judge the next coming up ActivityRecord data data, such as the app, the province will attempt to resume synchronized (mWindowManager. GetWindowManagerLock ()) {if (! next.visible || next.stopped || lastActivityTranslucent) { next.setVisibility(true); } next.startLaunchTickingLocked(); ActivityRecord lastResumedActivity = lastStack == null ? null :lastStack.mResumedActivity; final ActivityState lastState = next.getState(); mService.updateCpuStats(); next.setState(RESUMED, "resumeTopActivityInnerLocked"); mService.updateLruProcessLocked(next.app, true, null); updateLRUListLocked(next); mService.updateOomAdjLocked(); . try { final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread, next.appToken); // Deliver all pending results. ArrayList<ResultInfo> a = next.results; if (a ! = null) { final int N = a.size(); if (! next.finishing && N > 0) { transaction.addCallback(ActivityResultItem.obtain(a)); } } if (next.newIntents ! = null) { transaction.addCallback(NewIntentItem.obtain(next.newIntents, false /* andPause */)); } next.notifyAppResumed(next.stopped); next.sleeping = false; mService.getAppWarningsLocked().onResumeActivity(next); mService.showAskCompatModeDialogLocked(next); next.app.pendingUiClean = true; next.app.forceProcessStateUpTo(mService.mTopProcessState); next.clearOptionsLocked(); transaction.setLifecycleStateRequest( ResumeActivityItem.obtain(next.app.repProcState, mService.isNextTransitionForward())); mService.getLifecycleManager().scheduleTransaction(transaction); } the catch (Exception e) {/ / resume failed, then try to restart next. The setState (lastState, "resumeTopActivityInnerLocked"); if (lastResumedActivity ! = null) { lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked"); } if (! next.hasBeenLaunched) { next.hasBeenLaunched = true; } else if (SHOW_APP_STARTING_PREVIEW && lastStack ! = null && lastStack.isTopStackOnDisplay()) { next.showStartingWindow(null /* prev */, false /* newTask */, false /* taskSwitch */); } mStackSupervisor.startSpecificActivityLocked(next, true, false); return true; } } try { next.completeResumeLocked(); } catch (Exception e) { requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, "resume-exception", true); if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } } else { // Whoops, need to restart this activity! if (! next.hasBeenLaunched) { next.hasBeenLaunched = true; } else { if (SHOW_APP_STARTING_PREVIEW) { next.showStartingWindow(null /* prev */, false /* newTask */, false /* taskSwich */); } } mStackSupervisor.startSpecificActivityLocked(next, true, true); }... return true; }Copy the code

In this snippet, you can actually view it as doing two steps.

  • 1. First call to the onPause method of all activities in the Task stack.
  • 2. Check whether ActivityRecord is a reuse object. If yes, call back onResume; if no, prepare to start cross-process

In this scenario, ActivityRecord Next and ActivityRecord Prev are the same object. Because in the last article has confessed, will add ActivityRecord to Task in addOrReparentStartingActivity mActivities top.

So at this point the objects uploaded from the method and received by topRunningActivityLocked are actually our new ActivityRecord.

boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false); if (mResumedActivity ! = null) { pausing |= startPausingLocked(userLeaving, false, next, false); }Copy the code
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) { boolean someActivityPaused = false; for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx); for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = display.getChildAt(stackNdx); if (! isFocusedStack(stack) && stack.getResumedActivity() ! = null) { if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack + " mResumedActivity=" + stack.getResumedActivity()); someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming, dontWait); } } } return someActivityPaused; }Copy the code

And you can see that this is actually going to loop through to get ActivityStack in ActivityStackSupervisor and ActivityDisplay. Call the startPausingLocked method for each stack. The core method startPausingLocked is used to call the onPause method of an Activity.

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming, boolean pauseImmediately) { if (mPausingActivity ! = null) { if (! shouldSleepActivities()) { completePauseLocked(false, resuming); } } ActivityRecord prev = mResumedActivity; if (prev == null) { if (resuming == null) { mStackSupervisor.resumeFocusedStackTopActivityLocked(); } return false; } if (prev == resuming) { return false; } mPausingActivity = prev; mLastPausedActivity = prev; . if (prev.app ! = null && prev.app.thread ! = null) { try { mService.updateUsageStats(prev, false); mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, prev.configChangeFlags, pauseImmediately)); } catch (Exception e) { mPausingActivity = null; mLastPausedActivity = null; mLastNoHistoryActivity = null; } } else { mPausingActivity = null; mLastPausedActivity = null; mLastNoHistoryActivity = null; } if (! uiSleeping && ! mService.isSleepingOrShuttingDownLocked()) { mStackSupervisor.acquireLaunchWakelock(); } if (mPausingActivity ! = null) { if (! uiSleeping) { prev.pauseKeyDispatchingLocked(); } else if (DEBUG_PAUSE) { Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off"); } if (pauseImmediately) { completePauseLocked(false, resuming); return false; } else { schedulePauseTimeout(prev); return true; } } else { if (resuming == null) { mStackSupervisor.resumeFocusedStackTopActivityLocked(); } return false; }}Copy the code

We can see that this code is actually quite simple:

  • 1. First of all, judge the current need to stop, and need to interact with the ActivityRecord is not the same. If the ActivityRecord is the same, you do not need to stop the Activity on the App side.
  • 2. If the mResumedActivity app and app.thread are not empty, the ActivityRecord has been started. Try calling the onPause method on the Activity corresponding to the ActivityRecord across processes. The core approach is the following:
   mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
                        PauseActivityItem.obtain(prev.finishing, userLeaving,
                                prev.configChangeFlags, pauseImmediately));
Copy the code

As you can see, there’s a key object inside, PauseActivityItem. This object is used to communicate across processes, so it’s important to remember that this object, this method is the core method across processes, and I’ll come back to that later.

  • 3. If the mPausingActivity is not empty, use the flag bit to determine whether to call the current ActivityRecord passed by resume.

It’s worth noting here that there is an mResumedActivity object. This object is in fact in the method addOrReparentStartingActivity addActivityAtIndex will perform such a step, set the ActivityRecord set out, will call setTask method binding the current Task, One of them is this:

void setTask(TaskRecord task, boolean reparenting) { // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}. if (task ! = null && task == getTask()) { return; } final ActivityStack oldStack = getStack(); ActivityStack final ActivityStack newStack = task! = null ? task.getStack() : null; // Inform old stack (if present) of activity removal and new stack (if set) of activity // addition. if (oldStack ! = newStack) { if (! reparenting && oldStack ! = null) { oldStack.onActivityRemovedFromStack(this); } if (newStack ! = null) { newStack.onActivityAddedToStack(this); } } this.task = task; if (! reparenting) { onParentChanged(); }}Copy the code

When ActivityRecord is bound to TaskRecord, you can see that when ActivityRecord is switched to Stack or when ActivityRecord is not bound to TaskRecord, OnActivityAddedToStack is called, and mResumedActivity is set. It is also the energy step that determines which Activity is currently being resumed in startPausingLocked, while pause simply stops the Activity.

Check if ActivityRecord is a reuse object. If yes, call back onResume. If not, prepare to start cross-process

next.app ! = null && next.app.thread ! = nullCopy the code

We see the above code snippet first in the next code snippet. There are two situations:

  • 1. When the next ActivityRecord to be started is determined, there is no app(ProcessRecord) object or app.Thread (IApplicationThread represents ActivityRecord). Binding the object is a later step, so you can determine that it is a reusable Activity and try to call it directly
  transaction.setLifecycleStateRequest(
                            ResumeActivityItem.obtain(next.app.repProcState,
                                    mService.isNextTransitionForward()));
Copy the code

Call the onResume callback for an ActivityRecord corresponding to the Activity across processes.

  • 2. If the two objects are empty, it indicates that the new ActivityRecord has not been bound to the App. So it calls
mStackSupervisor.startSpecificActivityLocked(next, true, true);
Copy the code

ProcessRecord startSpecificActivityLocked detection

void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); getLaunchTimeTracker().setLaunchTime(r); if (app ! = null && app.thread ! = null) { try { if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 || !" android".equals(r.info.packageName)) { app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode, mService.mProcessStats); } realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { } } mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, "activity", r.intent.getComponent(), false, false, true); }Copy the code

The code in this section checks whether the Activity has the desired process. If it exists realStartActivityLocked is called to start cross-process. If not, startProcessLocked is called. Zygote connects to Zygote through socket. Zygote incubates a required process and starts startActivity again.

RealStartActivityLocked starts an Activity across processes

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException { if (! allPausedActivitiesComplete()) { return false; } final TaskRecord task = r.getTask(); final ActivityStack stack = task.getStack(); beginDeferResume(); try { r.startFreezingScreenLocked(app, 0); r.startLaunchTickingLocked(); r.setProcess(app); if (getKeyguardController().isKeyguardLocked()) { r.notifyUnknownVisibilityLaunched(); }... int idx = app.activities.indexOf(r); if (idx < 0) { app.activities.add(r); } mService.updateLruProcessLocked(app, true, null); mService.updateOomAdjLocked(); . try { if (app.thread == null) { throw new RemoteException(); } List<ResultInfo> results = null; List<ReferrerIntent> newIntents = null; if (andResume) { results = r.results; newIntents = r.newIntents; } if (r.isActivityTypeHome()) { // Home process is the root process of the task. mService.mHomeProcess = task.mActivities.get(0).app; }... // Create activity launch transaction. final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread, r.appToken); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global // and override configs. mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, newIntents, mService.isNextTransitionForward(), profilerInfo)); // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } clientTransaction.setLifecycleStateRequest(lifecycleItem); // Schedule transaction. mService.getLifecycleManager().scheduleTransaction(clientTransaction); . } catch (RemoteException e) { if (r.launchFailed) { mService.appDiedLocked(app); stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, "2nd-crash", false); return false; } r.launchFailed = true; app.activities.remove(r); throw e; } } finally { endDeferResume(); } r.launchFailed = false; if (stack.updateLRUListLocked(r)) { } .... return true; }Copy the code

Before the actual launch, AMS will first update the cache location of the process in the LRU, then update the application adj value, and then update the application priority. Start the Activity across processes only after these actions have been done.

ClientTransactionItem

These classes actually represent the state machines and states that AMS abstracts to control the remote life cycle of the App. The following classes are involved in the AMS cross-process control Activity lifecycle:

  • 1.ClientTransaction ClientTransaction controller
  • 2.ClientLifecycleManager Lifecycle transaction controller of the client
  • 3.TransactionExecutor a remote communication TransactionExecutor
  • 4.LaunchActivityItem onCreate Life cycle transaction in remote App
  • 5.ResumeActivityItem onResume lifecycle transaction on the remote App
  • 6.PauseActivityItem onPause life cycle transaction of the remote App
  • 7.StopActivityItem onStop life cycle transaction of remote App
  • 8.DestroyActivityItem Remote App onDestroy life cycle transaction.
  • 9. Handling of ClientTransaction ClientTransactionHandler App side.

Just listing it like this makes it easy to see how Google engineers have optimized the design of The Android lifecycle. And what you can see here is that we don’t see onStart and onRestart in the seven life cycles. In fact, onRestart reuses the LaunchActivityItem to restart the Activity, whereas onStart is only called locally by the App client after onCreate.

From here it is clear that all life cycles are inherited from the base class ClientTransactionItem. Whenever we try to operate across processes, we use the ClientTransactionItem base class. So in fact there are ActivityResultItem, NewIntentItem across processes operation etc.

With a little understanding of the structure of the classes, let’s try to look inside the source code. We pull out the source code for the Activity launch:

   final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
                        r.appToken);
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        // TODO: Have this take the merged configuration instead of separate global
                        // and override configs.
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
                        profilerInfo));

                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
 lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
                } else {
                 ...
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction.
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
Copy the code

In this section AMS will create a LaunchActivityItem as a callback and ResumeActivityItem will be set to the request at lifeCycle. Let’s track down the source scheduleTransaction.

void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            transaction.recycle();
        }
    }
Copy the code
    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }
Copy the code

We see that the IApplicationThread object, which is a Binder’s stub object, will be retrieved. The inner class of ActivityThread that implements it, ApplicationThread. From the UML diagram above, you can see that a ClientTransaction implements a Parcel and can therefore transfer the object across processes.

private class ApplicationThread extends IApplicationThread.Stub
Copy the code

ApplicationThread is a local Binder object. In AMS, this interface represents the remote proxy object. In other words, the scheduleTransaction method on the App side is called.

It’s actually in the higher version of Android

public final class ActivityThread extends ClientTransactionHandler
Copy the code

ActivityThread will inherit ClientTransactionHandler. Events are handled in the base class. In this case, we first go to the method in ApplicationThread:

 @Override
        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            ActivityThread.this.scheduleTransaction(transaction);
        }
Copy the code

At this point we can see that the parent class is still handling the method via the Handler mH:

void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }
Copy the code

This fragment is specifically handled in the mH handlerMessage

                case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);
                    if (isSystem()) {
                        transaction.recycle();
                    }
                    break;
Copy the code

TransactionExecutor

At this point, the App side will hand over the transaction that started the Activity to the TransactionExecutor.

    public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();

        executeCallbacks(transaction);

        executeLifecycleState(transaction);
        mPendingActions.clear();
    }
Copy the code

As you can see, there are two steps:

  • 1. First process the callback in the passed ClientTransaction
  • 2. Next process the LifecycleStateRequest in the ClientTransaction.

In the current scenario, the LaunchActivityItem is processed first and then the ResumeActivityItem is processed. That corresponds to onCreate and onResume. In fact, Google engineers divide each state into two steps. One is execute, followed by postExecute.

ExecuteCallbacks processing LaunchActivityItem

public void executeCallbacks(ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null) { return; } final IBinder token = transaction.getActivityToken(); ActivityClientRecord r = mTransactionHandler.getActivityClient(token); final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest(); final int finalState = finalStateRequest ! = null ? finalStateRequest.getTargetState() : UNDEFINED; // Index of the last callback that requests some post-execution state. final int lastCallbackRequestingState = lastCallbackRequestingState(transaction); final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); . item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); . }}Copy the code

The LaunchActivityItem communicates across processes to the ActivityThread

You can see that yes, the loop handles the added callback, and since there is only one object, LaunchActivityItem, execute and then postExecute.

    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client);
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
Copy the code

The client is the ActivityThread, which generates an ActivityClientRecord corresponding to the AMS ActivityRecord, and calls the handleLaunchActivity to invoke the onCreate method.

At this point, there is the familiar handleLaunchActivity method.

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ActivityInfo aInfo = r.activityInfo; . ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; try { java.lang.ClassLoader cl = appContext.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); . } catch (Exception e) { } try { Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity ! = null) { ... appContext.setOuterContext(activity); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback); . activity.mCalled = false; if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } if (! activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not  call through to super.onCreate()"); } r.activity = activity; } r.setState(ON_CREATE); mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { ... } return activity; }Copy the code

You can see that there are three steps:

    1. Reflection generates an Activity instance
  • 2. Obtain the Application object of the current Application and invoke attach binding
  • 3. Finally, call callActivityOnCreate through Instrument to invoke the onCreate method in the Activity instance.

Going back to LaunchActivityItem, the LaunchActivityItem postExecute does not implement any events, let’s look at the TransactionExecutor’s executeLifecycleState.

TransactionExecutor executeLifecycleState controls the onResume life cycle

private void executeLifecycleState(ClientTransaction transaction) {
        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
        if (lifecycleItem == null) {
            return;
        }

        final IBinder token = transaction.getActivityToken();
        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

        if (r == null) {
            return;
        }

        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }
Copy the code

Calling getLifecycleStateRequest at this point will fetch exactly the ResumeActivityItem that was set in earlier. The idea is similar to LaunchActivityItem in that it is executed in two steps, execute first, and then execute postExecute.

Call the onStart

But notice this cycleToPath.

 private void cycleToPath(ActivityClientRecord r, int finish,
            boolean excludeLastState) {
        final int start = r.getLifecycleState();
        final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
        performLifecycleSequence(r, path);
    }
Copy the code

This method will eventually call the following method:

public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {
        if (start == UNDEFINED || finish == UNDEFINED) {
            throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state");
        }
        if (start == ON_RESTART || finish == ON_RESTART) {
            throw new IllegalArgumentException(
                    "Can't start or finish in intermittent RESTART state");
        }
        if (finish == PRE_ON_CREATE && start != finish) {
            throw new IllegalArgumentException("Can only start in pre-onCreate state");
        }

        mLifecycleSequence.clear();
        if (finish >= start) {
            // just go there
            for (int i = start + 1; i <= finish; i++) {
                mLifecycleSequence.add(i);
            }
        } else { // finish < start, can't just cycle down
            if (start == ON_PAUSE && finish == ON_RESUME) {
....
            } else if (start <= ON_STOP && finish >= ON_START) {
                ....
            } else {
              ...
        }

  ...
        return mLifecycleSequence;
    }
Copy the code

Start is ON_CREATE and finish is ON_RESUME, so an intermediate ON_START method will be added to mLifecycleSequence.

At this point, performLifecycleSequence will execute the handleStartActivity method in the ActivityThread.

public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions) { final Activity activity = r.activity; if (r.activity == null) { // TODO(lifecycler): What do we do in this case? return; } if (! r.stopped) { throw new IllegalStateException("Can't start activity that is not stopped."); } if (r.activity.mFinished) { // TODO(lifecycler): How can this happen? return; } // Start activity.performStart("handleStartActivity"); r.setState(ON_START); if (pendingActions == null) { // No more work to do. return; } // Restore instance state if (pendingActions.shouldRestoreInstanceState()) { if (r.isPersistable()) { if (r.state ! = null || r.persistentState ! = null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); } } else if (r.state ! = null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); }}... }Copy the code

The Activity’s performStart method is called. This method will call the onStart method of the Fragment and the onStart method with Instrument.

ResumeActivityItem execute

 @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
                "RESUME_ACTIVITY");
    }
Copy the code

This moves to the ActivityThread’s handleResumeActivity method, which in turn calls performResumeActivity

public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest, String reason) { final ActivityClientRecord r = mActivities.get(token); . try { r.activity.onStateNotSaved(); r.activity.mFragments.noteStateNotSaved(); checkAndBlockForNetworkAccess(); if (r.pendingIntents ! = null) { deliverNewIntents(r, r.pendingIntents); r.pendingIntents = null; } if (r.pendingResults ! = null) { deliverResults(r, r.pendingResults, reason); r.pendingResults = null; } r.activity.performResume(r.startsNotResumed, reason); r.state = null; r.persistentState = null; r.setState(ON_RESUME); } catch (Exception e) { ... } return r; }Copy the code

I can see that if the pendingIntent data is not empty, then send a pendingIntent data back to onActivityResult, and then call the Activity’s onResume method.

The postExecute ResumeActivityItem

@Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { try { // TODO(lifecycler): Use interface callback instead of AMS. ActivityManager.getService().activityResumed(token); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}Copy the code

Service in this case refers to ActivityManagerService.

public final void activityResumed(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        synchronized(this) {
            ActivityRecord.activityResumedLocked(token);
            mWindowManager.notifyAppResumedFinished(token);
        }
        Binder.restoreCallingIdentity(origId);
    }
Copy the code

As you can see, the ActivityRecord state is set to resume state and the corresponding windowManager is set to Resume completed state.

This completes the Activity’s onResume method. Remember I wrote PauseActivityItem up there? This represents the pause lifecycle, so let’s explore the onPause process.

The execute PauseActivityItem processing

 mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
                        PauseActivityItem.obtain(prev.finishing, userLeaving,
                                prev.configChangeFlags, pauseImmediately));
Copy the code

Perform when we launched a new Activity, will use TransactionLifecycleManager its startup cross-process communication, then the principle and the above, we can directly to PauseActivityItem the execute method.

@Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
                "PAUSE_ACTIVITY_ITEM");
    }
Copy the code

This will reach the Activity’s onPause method. HandlePauseActivity will eventually reach performPauseActivity

private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, PendingTransactionActions pendingActions) { if (r.paused) { if (r.activity.mFinished) { return null; }... } if (finished) { r.activity.mFinished = true; } final boolean shouldSaveState = ! r.activity.mFinished && r.isPreHoneycomb(); if (shouldSaveState) { callActivityOnSaveInstanceState(r); } performPauseActivityIfNeeded(r, reason); ArrayList<OnActivityPausedListener> listeners; synchronized (mOnPauseListeners) { listeners = mOnPauseListeners.remove(r.activity); } int size = (listeners ! = null ? listeners.size() : 0); for (int i = 0; i < size; i++) { listeners.get(i).onPaused(r.activity); } final Bundle oldState = pendingActions ! = null ? pendingActions.getOldState() : null; if (oldState ! = null) { if (r.isPreHoneycomb()) { r.state = oldState; } } return shouldSaveState ? r.state : null; }Copy the code

As you can see, if the current state needs to be saved at this point, it will be saved in onPause. And wake up interfaces that are listening for onPause events. Through performPauseActivityIfNeeded will be transferred to the Activity. And notify the Fragment to set all states to onPause.

PauseActivityItem processing postExecute

@Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { if (mDontReport) { return; } try { ActivityManager.getService().activityPaused(token); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}Copy the code

What you can see is that activityPaused in AMS is called back and ActivityStack is told to call activityPausedLocked. Note that the token is IApplicationToken.Stub is used to uniquely identify the corresponding ActivityRecord and to notify the corresponding WindowManager to do further processing.

After calling onPause, notify AMS to call the onStop method.

In this activityPaused method, the completePauseLocked method is executed after the pause method is executed, and the onStop step is executed, after which the Activity’s onStop method is called.

private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) { ActivityRecord prev = mPausingActivity; if (prev ! = null) { prev.setWillCloseOrEnterPip(false); final boolean wasStopping = prev.isState(STOPPING); prev.setState(PAUSED, "completePausedLocked"); if (prev.finishing) { prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false, "completedPausedLocked"); } else if (prev.app ! = null) { if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) { } if (prev.deferRelaunchUntilPaused)  { prev.relaunchActivityLocked(false /* andResume */, prev.preserveWindowOnDeferredRelaunch); } else if (wasStopping) { prev.setState(STOPPING, "completePausedLocked"); } else if (! prev.visible || shouldSleepOrShutDownActivities()) { prev.setDeferHidingClient(false); addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */); } } else { prev = null; }... }... }Copy the code

What we can see is that once we determine that the current ActivityRecord is bound to the App, it’s started, and the current ActivityRecord visible is false, or we hit the lock screen to put it to sleep, we’ll call addToStopping, Set the current ActivityRecord to onStop. This flag is false if the Activity is not transparent by default. The scene is often in the Activity Dialog.

In addToStopping, will through the Handler ActivityStackSupervisorHandler onStop method processing.

final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout, boolean processPausingActivities, Configuration config) { if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token); ArrayList<ActivityRecord> finishes = null; ArrayList<UserState> startingUsers = null; int NS = 0; int NF = 0; boolean booting = false; boolean activityRemoved = false; ActivityRecord r = ActivityRecord.forTokenLocked(token); . for (int i = 0; i < NS; i++) { r = stops.get(i); final ActivityStack stack = r.getStack(); if (stack ! = null) { if (r.finishing) { stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false, "activityIdleInternalLocked"); } else { stack.stopActivityLocked(r); }}}... return r; }Copy the code

OnStop will be called across processes in ActivityStack

 final void stopActivityLocked(ActivityRecord r) {
     ...
        if (r.app != null && r.app.thread != null) {
            adjustFocusedActivityStack(r, "stopActivity");
            r.resumeKeyDispatchingLocked();
            try {
                r.stopped = false;
                r.setState(STOPPING, "stopActivityLocked");
                if (!r.visible) {
                    r.setVisible(false);
                }
                mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
                        StopActivityItem.obtain(r.visible, r.configChangeFlags));
                if (shouldSleepOrShutDownActivities()) {
                    r.setSleeping(true);
                }
                Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
                mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
            } catch (Exception e) {
            
                r.stopped = true;
                if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
                r.setState(STOPPED, "stopActivityLocked");
                if (r.deferRelaunchUntilPaused) {
                    destroyActivityLocked(r, true, "stop-except");
                }
            }
        }
    }
Copy the code

Here AGAIN I see the core lifecycle class StopActivityItem. Let’s look at the execute method and the postExecute method.

StopActivityItem the execute

@Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions,
                true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
    }
Copy the code

As you can see, the ActivityThread’s handleStopActivity is executed, and it calls the following method:

private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown, boolean saveState, boolean finalStateRequest, String reason) { if (r ! = null) { if (! keepShown && r.stopped) { if (r.activity.mFinished) { return; } if (! finalStateRequest) { ... } } performPauseActivityIfNeeded(r, reason); if (info ! = null) { try { info.setDescription(r.activity.onCreateDescription()); } catch (Exception e) { if (! mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to save state of activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } } if (! keepShown) { callActivityOnStop(r, saveState, reason); }}}Copy the code

CallActivityOnStop calls performStop, calls onStop for all fragments, and sets the WindowManagerGlobal state to paused. Finally, call the onStop method of the Activity with Instrument.

In activityStoppedLocked, check whether ActivityRecord has set finishing to true via makeFinishingLocked to determine whether subsequent cycles need to be executed.

When calling the Activity’s finish

It is common that when an Activity calls Finish, it will end up calling the onDestory lifecycle, so let’s explore the source code a little.

The finishActivity method in AMS is called when the developer calls the finish method

public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, int finishTask) { .... synchronized(this) { ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { return true; } TaskRecord tr = r.getTask(); ActivityRecord rootR = tr.getRootActivity(); . final long origId = Binder.clearCallingIdentity(); try { boolean res; final boolean finishWithRootActivity = finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY; if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY || (finishWithRootActivity && r == rootR)) { ... } else { res = tr.getStack().requestFinishActivityLocked(token, resultCode, resultData, "app-request", true); } return res; } finally { Binder.restoreCallingIdentity(origId); }}}Copy the code

At this point, our finishTask is usually not FINISH_TASK_WITH_ACTIVITY, so it usually goes to the following branch. At this time will get the current taskRecord corresponding ActivityStack, began to call requestFinishActivityLocked.

In requestFinishActivityLocked will pass token (above introduction IApplicationToken) to find the only labeled ActivityRecord. FinishActivityLocked is then called

final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, String reason, boolean oomAdj, boolean pauseImmediately) { if (r.finishing) { return false; } mWindowManager.deferSurfaceLayout(); try { r.makeFinishingLocked(); final TaskRecord task = r.getTask(); final ArrayList<ActivityRecord> activities = task.mActivities; final int index = activities.indexOf(r); if (index < (activities.size() - 1)) { task.setFrontOfTask(); if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) ! = 0) { ActivityRecord next = activities.get(index+1); next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); }} / / stop events distribution r.p auseKeyDispatchingLocked (); / / adjust focus ActivityStack adjustFocusedActivityStack (r, "finishActivity"); finishActivityResultsLocked(r, resultCode, resultData); final boolean endTask = index <= 0 && ! task.isClearingToReuseTask(); final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE; if (mResumedActivity == r) { .... r.setVisibility(false); if (mPausingActivity == null) { startPausingLocked(false, false, null, pauseImmediately); } if (endTask) { mService.getLockTaskController().clearLockedTask(task); } } else if (! r.isState(PAUSING)) { .... final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE : FINISH_AFTER_PAUSE; final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj, "finishActivityLocked") == null; . return removedActivity; } else { } return false; } finally { mWindowManager.continueSurfaceLayout(); }}Copy the code

From the finishActivityLocked method, there are actually two cases when finish. when

  • 1. The current Activity to finish just current are interactive Activity, call the onPause, during onStop activityIdleInternalLocked call, actually there is such a piece of code section,
for (int i = 0; i < NF; i++) {
            r = finishes.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) {
                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
            }
        }
Copy the code

You can see here that destroyActivityLocked will be called, calling the onDestroy method.

  • 2. When finish the Activity not onPause, finishCurrentActivityLocked calls a try and finish the corresponding Activity.
final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
            String reason) {

        final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
                true /* considerKeyguardState */);

        if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
                && next != null && !next.nowVisible) {
            if (!mStackSupervisor.mStoppingActivities.contains(r)) {
                addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */);
            }
            if (DEBUG_STATES) Slog.v(TAG_STATES,
                    "Moving to STOPPING: "+ r + " (finish requested)");
            r.setState(STOPPING, "finishCurrentActivityLocked");
            if (oomAdj) {
                mService.updateOomAdjLocked();
            }
            return r;
        }

        // make sure the record is cleaned out of other places.
        mStackSupervisor.mStoppingActivities.remove(r);
        mStackSupervisor.mGoingToSleepActivities.remove(r);
        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(r);
        final ActivityState prevState = r.getState();
        if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);

        r.setState(FINISHING, "finishCurrentActivityLocked");
        final boolean finishingActivityInNonFocusedStack
                = r.getStack() != mStackSupervisor.getFocusedStack()
                && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;

        if (mode == FINISH_IMMEDIATELY
                || (prevState == PAUSED
                    && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
                || finishingActivityInNonFocusedStack
                || prevState == STOPPING
                || prevState == STOPPED
                || prevState == ActivityState.INITIALIZING) {
            r.makeFinishingLocked();
            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);

....
            return activityRemoved ? null : r;
        }

....
        return r;
    }
Copy the code

As you can see, this will attempt to call addToStopping, which will call onStop, and then destroyActivityLocked, which will do the core of Finish.

destroyActivityLocked

final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) { if (r.isState(DESTROYING, DESTROYED)) { return false; } EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, r.userId, System.identityHashCode(r), r.getTask().taskId, r.shortComponentName, reason); boolean removedFromHistory = false; cleanUpActivityLocked(r, false, false); final boolean hadApp = r.app ! = null; if (hadApp) { if (removeFromApp) { r.app.activities.remove(r); if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) { mService.mHeavyWeightProcess = null; mService.mHandler.sendEmptyMessage( ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG); } if (r.app.activities.isEmpty()) { ... mService.updateLruProcessLocked(r.app, false, null); mService.updateOomAdjLocked(); } } boolean skipDestroy = false; try { mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken, DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)); } catch (Exception e) { if (r.finishing) { removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy"); removedFromHistory = true; skipDestroy = true; } } r.nowVisible = false; if (r.finishing && ! skipDestroy) { r.setState(DESTROYING, "destroyActivityLocked. finishing and not skipping destroy"); Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r); mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT); } else { r.setState(DESTROYED, "destroyActivityLocked. not finishing or skipping destroy"); r.app = null; } } else { if (r.finishing) { removeActivityFromHistoryLocked(r, reason + " hadNoApp"); removedFromHistory = true; } else { r.setState(DESTROYED, "destroyActivityLocked. not finishing and had no app"); r.app = null; } } r.configChangeFlags = 0; return removedFromHistory; }Copy the code

I can see that I’m familiar with DestroyActivityItem, which represents the class that calls the Activity’s onResume. Through the finish cross-process communication after this class, call removeActivityFromHistoryLocked, remove TaskRecord ActivityRecord, remove ActivityRecord the Window object, ProcessRecord, etc.

Again, let’s look at the Execute method on DestroyActivityItem.

DestroyActivityItem execute

public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        client.handleDestroyActivity(token, mFinished, mConfigChanges,
                false /* getNonConfigInstance */, "DestroyActivityItem");
    }
Copy the code

At this point, the ActivityThread’s handleDestroyActivity is called.

public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason); if (r ! = null) { cleanUpPendingRemoveWindows(r, finishing); WindowManager wm = r.activity.getWindowManager(); View v = r.activity.mDecor; if (v ! = null) { if (r.activity.mVisibleFromServer) { mNumVisibleActivities--; } IBinder wtoken = v.getWindowToken(); if (r.activity.mWindowAdded) { if (r.mPreserveWindow) { r.mPendingRemoveWindow = r.window; r.mPendingRemoveWindowManager = wm; r.window.clearContentView(); } else { wm.removeViewImmediate(v); } } if (wtoken ! = null && r.mPendingRemoveWindow == null) { WindowManagerGlobal.getInstance().closeAll(wtoken, r.activity.getClass().getName(), "Activity"); } else if (r.mPendingRemoveWindow ! = null) { WindowManagerGlobal.getInstance().closeAllExceptView(token, v, r.activity.getClass().getName(), "Activity"); } r.activity.mDecor = null; } if (r.mPendingRemoveWindow == null) { WindowManagerGlobal.getInstance().closeAll(token, r.activity.getClass().getName(), "Activity"); } Context c = r.activity.getBaseContext(); if (c instanceof ContextImpl) { ((ContextImpl) c).scheduleFinalCleanup( r.activity.getClass().getName(), "Activity"); } } if (finishing) { try { ActivityManager.getService().activityDestroyed(token); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } mSomeActivitiesChanged = true; }Copy the code

You can see that when we call OnDestroy on the Activity with performDestroyActivity, it will clear the window data set in the Activity and the ContentView set, Finally notify AMS via activityDestroyed.

After 10 seconds, a Handler message is sent to remove the ActivityRecord from the TaskRecord. If the TaskRecord does not have an ActivityRecord, it is removed from ActivityStack.

performDestroyActivity

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { ActivityClientRecord r = mActivities.get(token); Class<? extends Activity> activityClass = null; if (localLOGV) Slog.v(TAG, "Performing finish of " + r); if (r ! = null) { activityClass = r.activity.getClass(); r.activity.mConfigChangeFlags |= configChanges; if (finishing) { r.activity.mFinished = true; } performPauseActivityIfNeeded(r, "destroy"); if (! r.stopped) { callActivityOnStop(r, false /* saveState */, "destroy"); } if (getNonConfigInstance) { try { r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); } catch (Exception e) { if (! mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to retain activity " + r.intent.getComponent().toShortString() + ": " + e.toString(), e); } } } try { r.activity.mCalled = false; mInstrumentation.callActivityOnDestroy(r.activity); if (! r.activity.mCalled) { throw new SuperNotCalledException( "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onDestroy()"); } if (r.window ! = null) { r.window.closeAllPanels(); } } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (! mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to destroy activity " + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } r.setState(ON_DESTROY); } mActivities.remove(token); StrictMode.decrementExpectedActivityCount(activityClass); return r; }Copy the code

As you can see, onPause and onStop will be called once if no onPause and onStop have been called, but this is unlikely to happen, since both PauseActivityItem and StopActivityItem are called once. Finally, call callActivityOnDestroy through Instrument, call back to the Activity’s performDestroy, and finally close the Activity’s keyboard, clearing the cache of ActivityThread.

Call onDestroy on the Fragment in performDestroy, and call onDestroy on the Activity.

This completes the Activity’s onDestroy process.

onRestart

I’m sure some people are wondering, what about onRestart, one of the seven declaration cycles? It’s hidden in the TransactionExecutor just like onStart.

public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) { if (start == UNDEFINED || finish == UNDEFINED) { throw new IllegalArgumentException("Can't resolve lifecycle path for undefined state"); } if (start == ON_RESTART || finish == ON_RESTART) { throw new IllegalArgumentException( "Can't start or finish in intermittent RESTART state"); } if (finish == PRE_ON_CREATE && start ! = finish) { throw new IllegalArgumentException("Can only start in pre-onCreate state"); } mLifecycleSequence.clear(); if (finish >= start) { // just go there ... } else { // finish < start, can't just cycle down if (start == ON_PAUSE && finish == ON_RESUME) { // Special case when we can just directly go to resumed state. mLifecycleSequence.add(ON_RESUME); } else if (start <= ON_STOP && finish >= ON_START) { // Restart and go to required state. // Go to stopped state first. for (int i = start + 1; i <= ON_STOP; i++) { mLifecycleSequence.add(i); } // Restart mLifecycleSequence.add(ON_RESTART); // Go to required state for (int i = ON_START; i <= finish; i++) { mLifecycleSequence.add(i); } } else { // Relaunch and go to required state // Go to destroyed state first. for (int i = start + 1; i <= ON_DESTROY; i++) { mLifecycleSequence.add(i); } // Go to required state for (int i = ON_CREATE; i <= finish; i++) { mLifecycleSequence.add(i); }}}... return mLifecycleSequence; }Copy the code

Note that the start parameter refers to the state of the current Activity, and the finish parameter refers to what declaration cycle the target Activity for each ActivityLifecycleItem needs to reach after execution by the TransactionExecutor.

  • 1. If the current Activity isON_PAUSEThe state, the goal isON_RESUMEIn this case, only one needs to be executedON_RESUME
  • 2. If the status isON_STOPAnd the goal isON_STARTGenerally speaking, this time is the execution ofResumeActivityItemYou need to make the Activity visible from AMS. At this timeActivityI’ve already done onStop, so it’s going to be less thanON_STOP(if not, skip), and thenON_RESTARTThe declaration period is added, and then theonStartandonResume(because theResumeActivityItemGoal is toonResume) add in
  • 3. It finally reaches the TransactionExecutor to execute each oneActivityLifecycleItemThe life cycle of the ActivityThreadonRestartAfter implementation,onStart.onResume.

conclusion

This article focuses on Activity lifecycle processes. All lifecycle processes, instead of directly communicating with Binder across processes, are abstracted into a ClientTransactionItem for each transaction that needs to be processed by the Activity. And is processed by the ClientTransaction unified distribution. Every ClientTransactionItem is executed in two steps, execute and postExecute.

It’s safe to say that Android has never stopped thinking about Activity lifecycles, and has made life cycle state machine management more flexible through abstractions and the implementation of parcels.

After reading this article, you can see that the plugins I wrote earlier have no way to hook the Android OS directly, because a key step in the plugins’ base model is to hook the HandlerMessage method in the ActivityThread’s mH. However, the mH will not handle the life cycle, so there will be no way to replace the beam. But the way to handle the transaction is very simple, is the hook handlerMessage in the transaction signal.