Analyze the life cycle of the Activity after pressing the Back key from the source code (the life cycle after the page Activity ends)

Analyze the life cycle of the Activity after pressing the Back key from the source code (the life cycle after the page Activity ends)

The focus of this article is to analyze the source code and what happened after you pressed the Back key? Why is the life cycle of Activity like this?

Test the life cycle of Activity after pressing the Back key

First test the life cycle of the Activity after pressing the return button, where TestActivity is the second page Activity, MainActivity is the Activity
life cycle of the previous page, first TestActivity onPause, then the MainActivity process, wait for the MainActivity process to complete, and finally Take onStop and onDestroy of TestActivity

TestActivity: onBackPressed
TestActivity: onPause
MainActivity: onRestart
MainActivity: onStart
MainActivity: onResume
TestActivity: onStop
TestActivity: onDestroy

Analyze the life cycle of Activity after pressing the Back key from the source code

Since we want to analyze the life cycle after the Back key, of course we must start by pressing the Back key

public class MainActivity extends AppCompatActivity

public void onBackPressed() {
    super.onBackPressed();
}
public class AppCompatActivity extends FragmentActivity
public class FragmentActivity extends ComponentActivity

The inheritance relationship is not surprising, super.onBackPressed() is finally implemented by ComponentActivity

androidx\activity\ComponentActivity.java

public void onBackPressed() {
    mOnBackPressedDispatcher.onBackPressed();
}

Then it was distributed to the OnBackPressedDispatcher implementation

androidx\activity\OnBackPressedDispatcher.java

public void onBackPressed() {
    Iterator<OnBackPressedCallback> iterator =
            mOnBackPressedCallbacks.descendingIterator();
    while (iterator.hasNext()) {
        OnBackPressedCallback callback = iterator.next();
        if (callback.isEnabled()) {
            callback.handleOnBackPressed();
            return;
        }
    }
    if (mFallbackOnBackPressed != null) {
        mFallbackOnBackPressed.run();
    }
}

You can ignore the mOnBackPressedCallbacks part (added dynamically, it won’t be involved by pressing the Back key, so ignore it temporarily) to see what mFallbackOnBackPressed performs

androidx\activity\OnBackPressedDispatcher.java

private final Runnable mFallbackOnBackPressed;

public OnBackPressedDispatcher(@Nullable Runnable fallbackOnBackPressed) {
    mFallbackOnBackPressed = fallbackOnBackPressed;
}

In the end, I found it back to ComponentActivity and implemented the run method of OnBackPressedDispatcher

androidx\activity\ComponentActivity.java

private final OnBackPressedDispatcher mOnBackPressedDispatcher =
        new OnBackPressedDispatcher(new Runnable() {
            @Override
            public void run() {
                ComponentActivity.super.onBackPressed();
            }
        });

The final implementation of the onBackPressed method is still in the Activity, step by step, you can find the finishActivity method that finally calls ActivityManager.getService()

android\app\Activity.java

public void onBackPressed() {
    if (mActionBar != null && mActionBar.collapseActionView()) {
        return;
    }

    FragmentManager fragmentManager = mFragments.getFragmentManager();

    if (fragmentManager.isStateSaved() || !fragmentManager.popBackStackImmediate()) {
        finishAfterTransition();
    }
}

public void finishAfterTransition() {
    if (!mActivityTransitionState.startExitBackTransition(this)) {
        finish();
    }
}

public void finish() {
    finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}

private void finish(int finishTask) {
    if (mParent == null) {

        ...

        try {
            if (resultData != null) {
                resultData.prepareToLeaveProcess(this);
            }
            if (ActivityManager.getService()
                    .finishActivity(mToken, resultCode, resultData, finishTask)) {
                mFinished = true;
            }
        } catch (RemoteException e) {
            // Empty
        }
    } else {
        mParent.finishFromChild(this);
    }

    ...

}

ActivityManager.getService() can know that it is implemented by ActivityManagerService

The previous process knows that finishTask = FINISH_TASK_WITH_ROOT_ACTIVITY, so take the else branch

com\android\server\am\ActivityManagerService.java

public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
        int finishTask) {
    
    ...

        try {
            boolean res;
            final boolean finishWithRootActivity =
                    finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
            if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
                    || (finishWithRootActivity && r == rootR)) {
                res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
                        finishWithRootActivity, "finish-activity");
            } else {
            // finishTask = FINISH_TASK_WITH_ROOT_ACTIVITY, 所以走 else 分支
                res = tr.getStack().requestFinishActivityLocked(token, resultCode,
                        resultData, "app-request", true);
            }
            return res;
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
}

Enter ActivityStack, step by step to find the place of final realization

com\android\server\am\ActivityStack.java

final boolean requestFinishActivityLocked(IBinder token, int resultCode,
        Intent resultData, String reason, boolean oomAdj) {
    ActivityRecord r = isInStackLocked(token);
    if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(TAG_STATES,
            "Finishing activity token=" + token + " r="
            + ", result=" + resultCode + ", data=" + resultData
            + ", reason=" + reason);
    if (r == null) {
        return false;
    }

    finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
    return true;
}

final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
        String reason, boolean oomAdj) {
    return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
}


final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
        String reason, boolean oomAdj, boolean pauseImmediately) {
    // r.finishing = false
    if (r.finishing) {
        Slog.w(TAG, "Duplicate finish request for " + r);
        return false;
    }

    mWindowManager.deferSurfaceLayout();
    try {
        r.makeFinishingLocked();
        
        ...

        if (mResumedActivity == r) {
            
            ...

            if (mPausingActivity == null) {
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
                if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                        "finish() => pause with userLeaving=false");
                startPausingLocked(false, false, null, pauseImmediately);
            }

            if (endTask) {
                mService.getLockTaskController().clearLockedTask(task);
            }
        } else if (!r.isState(PAUSING)) {

            ...

    } finally {
        mWindowManager.continueSurfaceLayout();
    }
}

Find the constant of PAUSE_IMMEDIATELY in ActivityStackSupervisor, so pauseImmediately =! PAUSE_IMMEDIATELY = false,
no need to pause immediately

com\android\server\am\ActivityStackSupervisor.java

static final boolean PAUSE_IMMEDIATELY = true;

r.makeFinishingLocked in finishActivityLocked is to set finishing to true, so the value of r.finishing later is true

com\android\server\am\ActivityRecord.java

void makeFinishingLocked() {
    if (finishing) {
        return;
    }
    finishing = true;
    if (stopped) {
        clearOptionsLocked();
    }

    if (service != null) {
        service.mTaskChangeNotificationController.notifyTaskStackChanged();
    }
}

Next, start the life cycle of the real stop Activity startPausingLocked

com\android\server\am\ActivityStack.java

 * @param pauseImmediately True if the caller does not want to wait for the activity callback to
 *                         complete pausing.
// 如果不想要等待 activity 的回调,就设为 true,然后完成 pasue 操作
// 这里前面的流程知道 pauseImmediately = false, 想要 等待 activity 的回调,然后再完成 pasue 操作的
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
        ActivityRecord resuming, boolean pauseImmediately) {
    ...

    ActivityRecord prev = mResumedActivity;

    ...

    mPausingActivity = prev;
    
    ...

    if (prev.app != null && prev.app.thread != null) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
        try {
            EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
                    prev.shortComponentName, "userLeaving=" + userLeaving);
            mService.updateUsageStats(prev, false);
            // 具体执行 Activity 的 onPasue 生命周期
            mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
                    PauseActivityItem.obtain(prev.finishing, userLeaving,
                            prev.configChangeFlags, pauseImmediately));
        } catch (Exception e) { ... }
    } else { ... }

    ...

    if (mPausingActivity != null) {
        
        ...

        if (pauseImmediately) { ... } else {
            // 执行 Pause 如果超时的机制
            schedulePauseTimeout(prev);
            return true;
        }

    } else { ... }
}

Wait at most for Pause to time out 500 ms, and then the subsequent operations of Pause will be enforced. Because the operation is similar to the normal completion of the Pause process, we will not introduce it separately. If you are interested, you can see for yourself

com\android\server\am\ActivityStack.java

private void schedulePauseTimeout(ActivityRecord r) {
    final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
    msg.obj = r;
    r.pauseTime = SystemClock.uptimeMillis();
    mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
}

private static final int PAUSE_TIMEOUT = 500;

Take a look at how the onPause operation is
performed in PauseActivityItem and see that the operation in execute in PauseActivityItem is finally executed (this kind of operation that executes the Activity onPause life cycle, because it is similar to the onDestroy operation later, but simpler than the onDestroy process, just list OnDestroy The process of onPause is not redundant to explain. If you are interested, you can understand it yourself. I will not expand it specifically here.) After the end, I returned to ActivityManager.getService() == ActivityManagerService and performed Pause subsequent operations.

android \ app \ servertransaction \ PauseActivityItem.java

// Perform all actions that need to happen after execution, e.g. report the result to server.
// 所有的操作需要发生在执行之后
public void postExecute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    if (mDontReport) {
        return;
    }
    try {
        // TODO(lifecycler): Use interface callback instead of AMS.
        ActivityManager.getService().activityPaused(token);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

Going around, in fact, it still needs ActivityManagerService specific execution

com\android\server\am\ActivityManagerService.java

public final void activityPaused(IBinder token) {
    final long origId = Binder.clearCallingIdentity();
    synchronized(this) {
        ActivityStack stack = ActivityRecord.getStackLocked(token);
        if (stack != null) {
            stack.activityPausedLocked(token, false);
        }
    }
    Binder.restoreCallingIdentity(origId);
}

ActivityStack continues to perform the subsequent operations of activityPaused

com\android\server\am\ActivityStack.java

final void activityPausedLocked(IBinder token, boolean timeout) {
    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
        "Activity paused: token=" + token + ", timeout=" + timeout);

    final ActivityRecord r = isInStackLocked(token);
    if (r != null) {
        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
        if (mPausingActivity == r) {
            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
                    + (timeout ? " (due to timeout)" : " (pause complete)"));
            mService.mWindowManager.deferSurfaceLayout();
            try {
                completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
            } finally {
                mService.mWindowManager.continueSurfaceLayout();
            }
            return;
        } else { ... }
    }
    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}

Start and end Pause operation

  1. First end this Activity (that is, TestActivity). In fact, there is no real implementation of the life cycle of onStop and onDestroy. It just joins the queue to be ended and executes it when it is free.
  2. Start the life cycle of the previous Activity (that is, MainActivity)

com\android\server\am\ActivityStack.java

private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
    ActivityRecord prev = mPausingActivity;
    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);

    if (prev != null) {
        prev.setWillCloseOrEnterPip(false);
        final boolean wasStopping = prev.isState(STOPPING);
        prev.setState(PAUSED, "completePausedLocked");
        // 前面已经把 finishing 设置为 true
        if (prev.finishing) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
            // 1. 首先结束这个 Activity (也就是 TestActivity ),其实这里没有真正执行 onStop 和 onDestroy 的生命周期,只是加入要结束的队列,等空闲的时候真正执行
            prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
                    "completedPausedLocked");
        } else if (prev.app != null) { ... } else { ... }

        if (prev != null) {
            prev.stopFreezingScreenLocked(true /*force*/);
        }
        mPausingActivity = null;
    }

    if (resumeNext) {
        final ActivityStack topStack = mStackSupervisor.getFocusedStack();
        if (!topStack.shouldSleepOrShutDownActivities()) {
            // 2. 开始上一个 Activity 的生命周期(也就是 MainActivity )
            mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
        } else {
            checkReadyForSleep();
            ActivityRecord top = topStack.topRunningActivityLocked();
            if (top == null || (prev != null && top != prev)) {
                // If there are no more activities available to run, do resume anyway to start
                // something. Also if the top activity on the stack is not the just paused
                // activity, we need to go ahead and resume it to ensure we complete an
                // in-flight app switch.
                mStackSupervisor.resumeFocusedStackTopActivityLocked();
            }
        }
    }

    ...

}

Let’s take a look at how to end this Activity in the first step (that is, TestActivity)

The mode of the previous call method = FINISH_AFTER_VISIBLE, go directly to the if branch, and finally call the addToStopping method

The addToStopping method, in fact, only adds the current activity to mStoppingActivities, and does not immediately follow the life cycle of the activity

com\android\server\am\ActivityStack.java

final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
        String reason) {
    
    final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
            true /* considerKeyguardState */);

    // mode = FINISH_AFTER_VISIBLE, 直接走 if 分支
    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;
    }

    ...

}

void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
    if (!mStackSupervisor.mStoppingActivities.contains(r)) {
        mStackSupervisor.mStoppingActivities.add(r);
    }

    // If we already have a few activities waiting to stop, then give up
    // on things going idle and start clearing them out. Or if r is the
    // last of activity of the last task the stack will be empty and must
    // be cleared immediately.
    boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
            || (r.frontOfTask && mTaskHistory.size() <= 1);
    if (scheduleIdle || forceIdle) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle="
                + forceIdle + "immediate=" + !idleDelayed);
        if (!idleDelayed) {
            mStackSupervisor.scheduleIdleLocked();
        } else {
            mStackSupervisor.scheduleIdleTimeoutLocked(r);
        }
    } else {
        checkReadyForSleep();
    }
}

Then look at the beginning of the second step before the life cycle of the previous Activity (that is, MainActivity)

com\android\server\am\ActivityStackSupervisor.java

boolean resumeFocusedStackTopActivityLocked(
        ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {

    if (!readyToResume()) {
        return false;
    }

    if (targetStack != null && isFocusedStack(targetStack)) {
        return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
    }

    final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
    if (r == null || !r.isState(RESUMED)) {
        mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
    } else if (r.isState(RESUMED)) {
        // Kick off any lingering app transitions form the MoveTaskToFront operation.
        mFocusedStack.executeAppTransition(targetOptions);
    }

    return false;
}

resumeTopActivityInnerLocked This will take the life cycle of Activity's resume, so I won't go into details. If you are interested, you can see for yourself.

com\android\server\am\ActivityStack.java

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
    if (mStackSupervisor.inResumeTopActivity) {
        // Don't even start recursing.
        return false;
    }

    boolean result = false;
    try {
        // Protect against recursion.
        mStackSupervisor.inResumeTopActivity = true;
        result = resumeTopActivityInnerLocked(prev, options);

        final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
        if (next == null || !next.canTurnScreenOn()) {
            checkReadyForSleep();
        }
    } finally {
        mStackSupervisor.inResumeTopActivity = false;
    }

    return result;
}

Finished talking about the life cycle of TestActivity onPause and MainActivity onStart, onResume

Let's take a look at when TestActivity is onStop, onDestroy, and you can understand why the entire life cycle process is like this

Looking back through mStoppingActivities step by step, I finally found the source of removing the activity in mStoppingActivities in the idle Handler of ActivityThread's Idler, and Idler joined Looper when handleResumeActivity

After seeing this, you can understand why is the onStart of MainActivity after TestActivity onPause

We all know that IdleHandler has no messages in the Handler, and it will be executed when it is idle. Let's see how Idler involves removing the activity in mStoppingActivities.

android\app\ActivityThread.java

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    ...
    Looper.myQueue().addIdleHandler(new Idler());
}

private class Idler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() {
        ActivityClientRecord a = mNewActivities;
        boolean stopProfiling = false;
        if (mBoundApplication != null && mProfiler.profileFd != null
                && mProfiler.autoStopProfiler) {
            stopProfiling = true;
        }
        if (a != null) {
            mNewActivities = null;
            IActivityManager am = ActivityManager.getService();
            ActivityClientRecord prev;
            do {
                if (localLOGV) Slog.v(
                    TAG, "Reporting idle of " + a +
                    " finished=" +
                    (a.activity != null && a.activity.mFinished));
                if (a.activity != null && !a.activity.mFinished) {
                    try {
                        am.activityIdle(a.token, a.createdConfig, stopProfiling);
                        a.createdConfig = null;
                    } catch (RemoteException ex) {
                        throw ex.rethrowFromSystemServer();
                    }
                }
                prev = a;
                a = a.nextIdle;
                prev.nextIdle = null;
            } while (a != null);
        }
        if (stopProfiling) {
            mProfiler.stopProfiling();
        }
        ensureJitEnabled();
        return false;
    }
}

Finally, I found the activityIdle method in ActivityManagerService

com\android\server\am\ActivityManagerService.java

public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
    final long origId = Binder.clearCallingIdentity();
    synchronized (this) {
        ActivityStack stack = ActivityRecord.getStackLocked(token);
        if (stack != null) {
            ActivityRecord r =
                    mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
                            false /* processPausingActivities */, config);
            if (stopProfiling) {
                if ((mProfileProc == r.app) && mProfilerInfo != null) {
                    clearProfilerLocked();
                }
            }
        }
    }
    Binder.restoreCallingIdentity(origId);
}

Go back to ActivityStackSupervisor

  1. Call the processStoppingActivitiesLocked method to get the activity that needs to be removed (in fact, get the activity in mStoppingActivities)
  2. The life cycle of the execution activity

com\android\server\am\ActivityStackSupervisor.java

final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
        boolean processPausingActivities, Configuration config) {
    
    ...

    // Atomically retrieve all of the other things to do.
    final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
            true /* remove */, processPausingActivities);
    
    ...

    // Stop any activities that are scheduled to do so but have been
    // waiting for the next one to start.
    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);
            }
        }
    }

    ...

}

Let's look at the first step first, and see specifically how to get processStoppingActivitiesLocked

com\android\server\am\ActivityStackSupervisor.java

final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
        boolean remove, boolean processPausingActivities) {
    ArrayList<ActivityRecord> stops = null;

    final boolean nowVisible = allResumedActivitiesVisible();
    for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
        ActivityRecord s = mStoppingActivities.get(activityNdx);
        
        ...

                if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
                if (stops == null) {
                    stops = new ArrayList<>();
                }
                stops.add(s);
                mStoppingActivities.remove(activityNdx);
        }
    }

    return stops;
}

Look at the second step, how to execute the life cycle of the activity, end the activity

Here mode == FINISH_IMMEDIATELY, so follow the process below

com\android\server\am\ActivityStack.java

final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
        String reason) {
    
    ...

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

    // mode == FINISH_IMMEDIATELY, 所以走这里的流程
    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);

        ...
    }
    ...
}

Let's see how to end the activity

Method to execute DestroyActivityItem through ClientLifecycleManager

Actually see mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken, DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)); at this step, you know that Activity needs to execute the life cycle of onDestroy, but here There is still a bit of doubt, the onStop life cycle of Activity has not gone, why did it go onDestroy?

com\android\server\am\ActivityStack.java

final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {

        ...

        try {
            if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
            mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
                    DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
        } catch (Exception e) {
            // We can just ignore exceptions here...  if the process
            // has crashed, our death notification will clean things
            // up.
            //Slog.w(TAG, "Exception thrown during finish", e);
            if (r.finishing) {
                removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
                removedFromHistory = true;
                skipDestroy = true;
            }
        }

        ...
}

Take a look at how ClientLifecycleManager is executed

com\android\server\am\ClientLifecycleManager.java

void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,
        @NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
    final ClientTransaction clientTransaction = transactionWithState(client, activityToken,
            stateRequest);
    scheduleTransaction(clientTransaction);
}

void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    final IApplicationThread client = transaction.getClient();
    transaction.schedule();
    if (!(client instanceof Binder)) {
        // If client is not an instance of Binder - it's a remote call and at this point it is
        // safe to recycle the object. All objects used for local calls will be recycled after
        // the transaction is executed on client in ActivityThread.
        transaction.recycle();
    }
}

In the end, it is still executed by the ApplicationThread of ActivityThread

android\app\servertransaction\ClientTransaction.java

private IApplicationThread mClient;

public void schedule() throws RemoteException {
    mClient.scheduleTransaction(this);
}

Still returned to the execution of ActivityThread

android\app\ActivityThread.java

private class ApplicationThread extends IApplicationThread.Stub {
    public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        ActivityThread.this.scheduleTransaction(transaction);
    }
}    

Look at the inheritance relationship, and take the scheduleTransaction method in ClientTransactionHandler

android\app\ClientTransactionHandler.java
public final class ActivityThread extends ClientTransactionHandler

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

sendMessage is finally sent to the Handler named H in ActivityThread

android\app\ActivityThread.java

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    if (DEBUG_MESSAGES) Slog.v(
        TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
        + ": " + arg1 + " / " + obj);
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

final H mH = new H();

class H extends Handler {
    public void handleMessage(Message msg) {
        case EXECUTE_TRANSACTION:
            final ClientTransaction transaction = (ClientTransaction) msg.obj;
            mTransactionExecutor.execute(transaction);
            if (isSystem()) {
                // Client transactions inside system process are recycled on the client side
                // instead of ClientLifecycleManager to avoid being cleared before this
                // message is handled.
                transaction.recycle();
            }
            // TODO(lifecycler): Recycle locally scheduled transactions.
            break;
    }
}

Step by step, know where to implement

execute --> executeLifecycleState --> cycleToPath

android\app\servertransaction\TransactionExecutor.java

public void execute(ClientTransaction transaction) {
    final IBinder token = transaction.getActivityToken();
    log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);

    executeCallbacks(transaction);

    executeLifecycleState(transaction);
    mPendingActions.clear();
    log("End resolving transaction");
}


private void executeLifecycleState(ClientTransaction transaction) {
    final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
    if (lifecycleItem == null) {
        // No lifecycle request, return early.
        return;
    }
    log("Resolving lifecycle state: " + lifecycleItem);

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

    if (r == null) {
        // Ignore requests for non-existent client records for now.
        return;
    }

    // Cycle to the state right before the final requested state.
    cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);

    // Execute the final transition with proper parameters.
    lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
    lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}


private void cycleToPath(ActivityClientRecord r, int finish,
        boolean excludeLastState) {
    final int start = r.getLifecycleState();
    log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
    final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
    performLifecycleSequence(r, path);
}

First look at the screenshot of the next debugging, you can see start = 4, finish = 6

See the definition in ActivityLifecycleItem to know that start = ON_PAUSE, finish = ON_DESTROY

Insert picture description here

android\app\servertransaction\ActivityLifecycleItem.java

public abstract class ActivityLifecycleItem extends ClientTransactionItem {
    public static final int UNDEFINED = -1;
    public static final int PRE_ON_CREATE = 0;
    public static final int ON_CREATE = 1;
    public static final int ON_START = 2;
    public static final int ON_RESUME = 3;
    public static final int ON_PAUSE = 4;
    public static final int ON_STOP = 5;
    public static final int ON_DESTROY = 6;
    public static final int ON_RESTART = 7;
}

If you don’t understand why this is the case, take a look at getTargetState = ON_DESTROY obtained in DestroyActivityItem, and TestActivity has gone onPause life cycle, but has not gone onStop life cycle, so start = ON_PAUSE

android\app\servertransaction\DestroyActivityItem.java

public class DestroyActivityItem extends ActivityLifecycleItem {
    @Override
    public int getTargetState() {
        return ON_DESTROY;
    }
}

getLifecyclePath is to get the life cycle trajectory, in fact, it is to go through the complete life cycle process, so this setting will not jump from onPause to onDestroy, and must go through onStop

android\app\servertransaction\TransactionExecutorHelper.java

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) {
            // 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);
            }
        }
    }

    // Remove last transition in case we want to perform it with some specific params.
    if (excludeLastState && mLifecycleSequence.size() != 0) {
        mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
    }

    return mLifecycleSequence;
}

Through the for loop, execute from the beginning to the position one by one in order, that is, onStop first and then onDestroy

android\app\servertransaction\TransactionExecutor.java

 private void performLifecycleSequence(ActivityClientRecord r, IntArray path) {
    final int size = path.size();
    for (int i = 0, state; i < size; i++) {
        state = path.get(i);
        switch (state) {
            ...
            case ON_STOP:
                mTransactionHandler.handleStopActivity(r.token, false /* show */,
                        0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
                        "LIFECYCLER_STOP_ACTIVITY");
                break;
            case ON_DESTROY:
                mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
                        0 /* configChanges */, false /* getNonConfigInstance */,
                        "performLifecycleSequence. cycling to:" + path.get(size - 1));
                break;
                ...
        }
    }
}

The following is very simple, is how to execute the onStop method of Activity step by step, so I will not introduce them one by one.

android\app\ActivityThread.java
public final class ActivityThread extends ClientTransactionHandler

public void handleStopActivity(IBinder token, boolean show, int configChanges,
        PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
    final ActivityClientRecord r = mActivities.get(token);
    r.activity.mConfigChangeFlags |= configChanges;

    final StopInfo stopInfo = new StopInfo();
    performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
            reason);

    ...
}


private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
        boolean saveState, boolean finalStateRequest, String reason) {
    if (r != null) {
        ...

        if (!keepShown) {
            callActivityOnStop(r, saveState, reason);
        }
    }
}

private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
    
    ...

    try {
        r.activity.performStop(false /*preserveWindow*/, reason);
    } catch (SuperNotCalledException e) { ... } 

    ...
}

android\app\Activity.java

final void performStop(boolean preserveWindow, String reason) {
    mDoReportFullyDrawn = false;
    mFragments.doLoaderStop(mChangingConfigurations /*retain*/);

    // Disallow entering picture-in-picture after the activity has been stopped
    mCanEnterPictureInPicture = false;

    if (!mStopped) {
        ....
        mInstrumentation.callActivityOnStop(this);
        ...
    }
    mResumed = false;
}

android\app\Instrumentation.java

public void callActivityOnStop(Activity activity) {
    activity.onStop();
}

android\app\Activity.java

protected void onStop() {
    ...
}

to sum up

Why does the life cycle of MainActivity onStart, onResume execute after TestActivity onPause?

In fact, after TestActivity onPause is executed, only the TestActivity to be suspended is added to mStoppingActivities, waiting for the Handler to be idle, and it must be idle after MainActivity onResume, and then execute TestActivity's onStop and onDestroy.

TestActivity onPause immediately executes MainActivity onStart, onResume, so the Handler is not idle, and of course it will not execute TestActivity's onStop and onDestroy immediately.

In fact, this is understandable. After TestActivity onPause, the APP will have no screen immediately, and the next MainActivity needs to be executed first. Keep the APP with a screen, which is equivalent to high priority. TestActivity can be processed when the backside is idle.

Guess you like

Origin blog.csdn.net/qq_16927853/article/details/109069245