当前位置: 首页 > news >正文

【Android 源码分析】Activity生命周期之onStop-1

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的IT男

本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815

正文

生命周期系列:

  • Activity生命周期之onPause

  • onCreate,onStart,onResume-1

  • onCreate,onStart,onResume-2

  • Activity生命周期之onStop-1

  • Activity生命周期之onStop-2

  • Activity生命周期之onDestory

之前看过一遍流程的Events日志

// SystemService端创建TargetActivity  (writeWmCreateActivity)
03-27 14:41:06.428 27889 28629 I wm_create_activity: [0,253598020,21,com.google.android.dialer/.extensions.GoogleDialtactsActivity,android.intent.action.MAIN,NULL,NULL,270532608]// SystemService端触发SourceActivity的Pause (writeWmPauseActivity)
03-27 14:41:06.431 27889 28629 I wm_pause_activity: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher,userLeaving=true,pauseBackTasks]// SourceActivity应用端将执行onPause (writeWmOnPausedCalled)
03-27 14:41:06.448 28606 28606 I wm_on_paused_called: [51114540,com.android.launcher3.uioverrides.QuickstepLauncher,performPause]// SystemService端把SourceActivity添加进需要stop的集合 (writeWmAddToStopping)
03-27 14:41:06.459 27889 28630 I wm_add_to_stopping: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher,makeInvisible]// SystemService端要真正触发TargetActivity启动 (writeWmRestartActivity)
03-27 14:41:06.487 27889 28630 I wm_restart_activity: [0,253598020,21,com.google.android.dialer/.extensions.GoogleDialtactsActivity]// TargetActivity端将执行onCreate  (writeWmOnCreateCalled)
03-27 14:41:06.769  3401  3401 I wm_on_create_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,performCreate]// TargetActivity端将执行onStart (writeWmOnStartCalled)
03-27 14:41:06.900  3401  3401 I wm_on_start_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,handleStartActivity]// TargetActivity端将执行Resume (writeWmOnResumeCalled)
03-27 14:41:06.911  3401  3401 I wm_on_resume_called: [253598020,com.google.android.dialer.extensions.GoogleDialtactsActivity,RESUME_ACTIVITY]// SystemService端触发SourceActivity的stop (writeWmStopActivity)
03-27 14:41:07.097 27889 30491 I wm_stop_activity: [0,51114540,com.android.launcher3/.uioverrides.QuickstepLauncher]// SourceActivity应用端将执行onStop (writeWmStopActivity)
03-27 14:41:07.119 28606 28606 I wm_on_stop_called: [51114540,com.android.launcher3.uioverrides.QuickstepLauncher,STOP_ACTIVITY_ITEM]

其中可以看到很早就打印了 “wm_add_to_stopping”,但是 SourceActivity 真正执行 onPause (“wm_on_stop_called”)却是在最后。

所以按照顺序,先看看 “wm_add_to_stopping”相关的逻辑。

整个流程分为3步:

    1. addToStopping 流程,将应用添加进需要stop的列表 – system_service进程处理
    1. stopIfPossible 流程,也就是开始执行stop – system_service进程处理
    1. 应用端处理stop流程 – 应用进程处理

前面个都是system_service进程处理的,第三步是在 SourceActivity 的应用进程处理。
本篇为 onStop第一篇,介绍 system_service 进程是如何把 SourceActivity 添加进stop集合。

第一步:addToStopping 流程

1 调用链概览

通过Events日志可知“wm_add_to_stopping”的执行在真正启动 TargetActivity 之前。 在 SourceActivity 的 pause 流程之后。

“wm_add_to_stopping” 日志打印的方法在 ActivityRecord::addToStopping 中,在这个方法加上堆栈得到以下内容

03-28 14:39:03.326 23968 26756 I wm_add_to_stopping: [0,264636779,com.android.launcher3/.uioverrides.QuickstepLauncher,makeInvisible]
03-28 14:39:03.327 23968 26756 E biubiubiu: ActivityRecord  addToStopping: ActivityRecord{fc6096b u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t7}
03-28 14:39:03.327 23968 26756 E biubiubiu: java.lang.Exception
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.ActivityRecord.addToStopping(ActivityRecord.java:6225)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.ActivityRecord.makeInvisible(ActivityRecord.java:5811)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.EnsureActivitiesVisibleHelper.setActivityVisibilityState(EnsureActivitiesVisibleHelper.java:243)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.EnsureActivitiesVisibleHelper.process(EnsureActivitiesVisibleHelper.java:155)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.TaskFragment.updateActivityVisibilities(TaskFragment.java:1118)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.Task.lambda$ensureActivitiesVisible$19(Task.java:4918)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.Task$$ExternalSyntheticLambda24.accept(Unknown Source:10)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.Task.forAllLeafTasks(Task.java:3183)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.Task.forAllLeafTasks(Task.java:3171)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.Task.ensureActivitiesVisible(Task.java:4917)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.DisplayContent.lambda$ensureActivitiesVisible$44(DisplayContent.java:6143)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.DisplayContent$$ExternalSyntheticLambda38.accept(Unknown Source:10)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.Task.forAllRootTasks(Task.java:3195)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2033)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2026)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.DisplayContent.ensureActivitiesVisible(DisplayContent.java:6142)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.RootWindowContainer.ensureActivitiesVisible(RootWindowContainer.java:1873)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.RootWindowContainer.ensureActivitiesVisible(RootWindowContainer.java:1854)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.TaskFragment.completePause(TaskFragment.java:1802)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.ActivityRecord.activityPaused(ActivityRecord.java:6063)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.ActivityClientController.activityPaused(ActivityClientController.java:182)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:574)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:125)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at android.os.Binder.execTransactInternal(Binder.java:1280)
03-28 14:39:03.327 23968 26756 E biubiubiu: 	at android.os.Binder.execTransact(Binder.java:1244)

根据这段堆栈很明显可以看初是 SourceActivity 执行的 activityPaused 流程,再根据之前的Events日志顺序,也知道是在 SourceActivity 执行完 pause 流程后触发的。

也就是说 activityPaused 流程再触发 TargetActivity 启动前,还执行了 addToStopping 流程,将 SourceActivity 放进来需要 stop 的列表中。

关于 【Activity启动流程-2】流程之前整理的调用链如下:

ActivityClientController::activityPausedActivityRecord::activityPausedTaskFragment::completePauseRootWindowContainer::resumeFocusedTasksTopActivities       --分支1 ,再次执行 resumeFocusedTasksTopActivitiesRootWindowContainer::resumeFocusedTasksTopActivitiesTask::resumeTopActivityUncheckedLockedTask::resumeTopActivityInnerLockedTaskFragment::resumeTopActivityActivityTaskSupervisor::startSpecificActivity                           --触发 startSpecificActivity 判断应用是否创建RootWindowContainer::ensureActivitiesVisible              --分支2RootWindowContainer::ensureActivitiesVisibleDisplayContent::ensureActivitiesVisible WindowContainer::forAllRootTasks  --忽略固定逻辑Task::ensureActivitiesVisibleTask::forAllLeafTasks --忽略固定逻辑TaskFragment::updateActivityVisibilitiesEnsureActivitiesVisibleHelper::processEnsureActivitiesVisibleHelper::setActivityVisibilityStateEnsureActivitiesVisibleHelper::makeVisibleAndRestartIfNeededActivityTaskSupervisor::startSpecificActivity       --触发 startSpecificActivity 判断应用是否创建

这2个分支在【Activity启动流程】都分析过了,但是现在关注的是生命周期相关, 分之一和生命周期相关的流程已经分析过了,现在需要看看分支2和生命周期相关的逻辑。

# TaskFragmentvoid completePause(boolean resumeNext, ActivityRecord resuming) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "completePause");// 拿到之前的Activity,也就是需要 pause的ActivityRecord prev = mPausingActivity;ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);if (prev != null) {......// 设置窗口状态为PAUSEDprev.setState(PAUSED, "completePausedLocked");if (prev.finishing) {...... 如果已经finish,上面还在设置 pause,那正常应该是还没finish} else if (prev.hasProcess()) {// 打印状态日志ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "+ "wasStopping=%b visibleRequested=%b",  prev,  wasStopping,prev.mVisibleRequested);}else {......}if (resumeNext) {......// 重点* 第1分支Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resumeFocusedTasksTopActivities");mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,null /* targetOptions */);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);......}......// 重点* 第2分支Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ensureActivitiesVisible");mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);......}Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}

我在代码上加了几个 Trace 稍后再看,当前分析 onStop 流程所以只看第2分支触发的 ensureActivitiesVisible 流程,这个流程就是界面上有啥风吹草动,SystemService 端需要确保手机屏幕上Activity正确的显示。

根据上面的堆栈和整理出的调用链 RootWindowContainer::ensureActivitiesVisible 最终会执行到 EnsureActivitiesVisibleHelper::process 来处理当前 Task 下 Activity 的可见性。

EnsureActivitiesVisibleHelper的相关处理后面单独放一个章节详细解释,是ensureActivitiesVisible 流程非常重要的一环,目前可以自行了解或以黑盒概念理解

直接看 EnsureActivitiesVisibleHelper::setActivityVisibilityState 对 SourceActivity 的 stop 逻辑是如何处理的。

# EnsureActivitiesVisibleHelper// 当前Task栈顶是否有Activityprivate boolean mAboveTop;// 设置Activity可见性private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,final boolean resumeTopActivity) {......// SourceActivity 所在的Task 被遮挡了,所以mBehindFullyOccludedContainer为truer.updateVisibilityIgnoringKeyguard(mBehindFullyOccludedContainer);// 自然是返回不可见final boolean reallyVisible = r.shouldBeVisibleUnchecked();......if (reallyVisible) {// 忽略可见处理} else {if (DEBUG_VISIBILITY) {Slog.v(TAG_VISIBILITY, "Make invisible? " + r+ " finishing=" + r.finishing + " state=" + r.getState()+ " containerShouldBeVisible=" + mContainerShouldBeVisible+ " behindFullyOccludedContainer=" + mBehindFullyOccludedContainer+ " mLaunchTaskBehind=" + r.mLaunchTaskBehind);}// 不可见的处理 --使Activity不可见r.makeInvisible();}......// launcher处理}

所以执行 ActivityRecord::makeInvisible 方法开始设置 SourceActivity 不可见。

2 设置Activity不可见 – ActivityRecord::makeInvisible

makeInvisible 方法触发的调用链:

ActivityRecord::makeInvisibleActivityRecord::setVisibility  -- setVisibility通用流程ActivityRecord::setVisibilityActivityRecord::setVisibleRequestedActivityRecord::addToStoppingActivityTaskSupervisor.mStoppingActivities::add --添加当前Activity到集合ActivityTaskSupervisor::scheduleIdle  -- 发送IDLE_NOW_MSG--------------处理消息-------------------ActivityTaskSupervisor$ActivityTaskSupervisorHandler::handleMessageActivityTaskSupervisor$ActivityTaskSupervisorHandler::handleMessageInnerActivityTaskSupervisor$ActivityTaskSupervisorHandler::activityIdleFromMessageActivityTaskSupervisor::activityIdleInternalActivityTaskSupervisor::processStoppingAndFinishingActivities -- 热启动第一次执行到这时因条件不满足不会执行后续逻辑

看一下代码执行流程。

# ActivityRecordvoid makeInvisible() {// mVisibleRequested 为false表示当前 Activity 已经在处理可见性逻辑了,不需要执行后面的逻辑, if (!mVisibleRequested) {if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);return;}// Now for any activities that aren't visible to the user, make sure they no longer are// keeping the screen frozen.// 对于不可见的Activity,需要开始真正的处理了if (DEBUG_VISIBILITY) {Slog.v(TAG_VISIBILITY, "Making invisible: " + this + ", state=" + getState());}try {final boolean canEnterPictureInPicture = checkEnterPictureInPictureState("makeInvisible", true /* beforeStopping */);// Defer telling the client it is hidden if it can enter Pip and isn't current paused,// stopped or stopping. This gives it a chance to enter Pip in onPause().// 画中画相关final boolean deferHidingClient = canEnterPictureInPicture&& !isState(STARTED, STOPPING, STOPPED, PAUSED);setDeferHidingClient(deferHidingClient);// 重点* 1. 设置为不可见 ,会将 mVisibleRequested 设置为falsesetVisibility(false);switch (getState()) {case STOPPING:case STOPPED:// Reset the flag indicating that an app can enter picture-in-picture once the// activity is hiddensupportsEnterPipOnTaskSwitch = false;break;case RESUMED:case INITIALIZING:case PAUSING:case PAUSED:case STARTED:// 重点* 2. 加入到Stop列表addToStopping(true /* scheduleIdle */,canEnterPictureInPicture /* idleDelayed */, "makeInvisible");break;default:break;}} catch (Exception e) {// Just skip on any failure; we'll make it visible when it next restarts.Slog.w(TAG, "Exception thrown making hidden: " + intent.getComponent(), e);}}
    1. 调用 setVisibility 方法设置为不可见
    1. 根据 state 做了一些处理,STOPPING 和 STOPPED 的情况下就设置了一个画中画的标志位,其他的几个状态会执行 addToStopping

方法前面的 mVisibleRequested 唯一赋值的地方在 ActivityRecord::setVisibleRequested 方法中,值为可见性,所以这个变量表示: 当前Activity被设置的可见性的值。

makeInvisible 方法是要将 Activity 设置为不可见,所以方法开始的时候如果 mVisibleRequested 是 false, 说明已经在做这件事了, 就不需要执行后面的逻辑了。

然后中间有一段画中画的逻辑处理,这段当前不需要关注。

再下面就是getState() 的返回值处理,当前执行到这里的是 SourceActivity 的 activityPaused 流程,所以这里的 getState() 返回的是 PAUSED ,这个可以通过上面的log确认。

2.1 setVisibility 方法

看一眼 setVisibility方法主要做了什么

# ActivityRecordvoid setVisibility(boolean visible, boolean deferHidingClient) {......ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,"setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",token, visible, appTransition, isVisible(), mVisibleRequested,Debug.getCallers(6));......// 处理 DisplayContent 下的2个集合displayContent.mOpeningApps.remove(this);displayContent.mClosingApps.remove(this);......// 1. 给 mVisibleRequested 赋值setVisibleRequested(visible);......// 2. 处理 DisplayContent 下的2个集合if (visible) {displayContent.mOpeningApps.add(this);mEnteringAnimation = true;} else if (mVisible) {displayContent.mClosingApps.add(this);mEnteringAnimation = false;}......// 3. 提交可见性更新commitVisibility(visible, true /* performLayout */);updateReportedVisibilityLocked();}
    1. 执行 setVisibleRequested 方法,内部会对 mVisibleRequested 变量赋值
    1. 处理 DisplayContent类维护的2个列表, 分别对应打开和关闭的应用(这个很重要)
    1. 执行 commitVisibility 方法,这里会最终可见性

继续看主流程 addToStopping 方法。

addToStopping 流程非常重要

3. ActivityRecord::addToStopping 方法

# ActivityRecordfinal ActivityTaskSupervisor mTaskSupervisor;void addToStopping(boolean scheduleIdle, boolean idleDelayed, String reason) {if (!mTaskSupervisor.mStoppingActivities.contains(this)) {// 打印 event日志EventLogTags.writeWmAddToStopping(mUserId, System.identityHashCode(this),shortComponentName, reason);// 重点* 1. 唯一将Activity添加到 mStoppingActivities 集合中的代码mTaskSupervisor.mStoppingActivities.add(this);}final Task rootTask = getRootTask();......if (scheduleIdle || forceIdle) {// 打印Proto日志ProtoLog.v(WM_DEBUG_STATES,"Scheduling idle now: forceIdle=%b immediate=%b", forceIdle, !idleDelayed);if (!idleDelayed) {// 重点* 2. 一般都是立即执行,所以是走这(可见根据上面log的immediate值确认)mTaskSupervisor.scheduleIdle();} ......} ......}

这个方法做了2件事

    1. 将当前这个Activity 添加到 ActivityTaskSupervisor 下的 mStoppingActivities 集合
    1. 执行 ActivityTaskSupervisor::scheduleIdle 内部会发送 “IDLE_NOW_MSG” 消息,进而触发后续 stop 逻辑

这个方法正常是执行 scheduleIdle 逻辑的, 另外有 Events 和Proto 2个日志的打印,如果发现有问题可以仔细看看相关变量的打印。

ActivityTaskSupervisor::scheduleIdle 方法如下:

# ActivityTaskSupervisorfinal void scheduleIdle() {if (!mHandler.hasMessages(IDLE_NOW_MSG)) {// 发送消息,打堆栈if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdle: Callers=" + Debug.getCallers(4));mHandler.sendEmptyMessage(IDLE_NOW_MSG);}}

这个方法主要就是发送了“IDLE_NOW_MSG” 消息,并且打印了debug的堆栈。

冷启动的话 ActivityTaskSupervisor::scheduleIdle 的调用链就上面分析的一个,但是应用内启动Activity 根据log打印的堆栈发现会有3个调用链,这边也整理一下3个调用链,感兴趣的可以自己跟一下

Callers=com.android.server.wm.ActivityRecord.addToStopping:6239com.android.server.wm.ActivityRecord.makeInvisible:5810com.android.server.wm.EnsureActivitiesVisibleHelper.setActivityVisibilityState:240com.android.server.wm.EnsureActivitiesVisibleHelper.process:153 
Callers=com.android.server.wm.ActivityTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded:2175com.android.server.wm.ActivityRecord.onAnimationFinished:7473com.android.server.wm.WindowContainer$$ExternalSyntheticLambda4.onAnimationFinished:2com.android.server.wm.SurfaceAnimator.lambda$getFinishedCallback$0$com-android-server-wm-SurfaceAnimator:133 
Callers=com.android.server.wm.ActivityTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded:2175com.android.server.wm.ActivityRecord.onWindowsVisible:6532com.android.server.wm.ActivityRecord.updateReportedVisibilityLocked:6633com.android.server.wm.WindowManagerService$4.onAppTransitionFinishedLocked:1154 

4 “IDLE_NOW_MSG”的处理 ActivityTaskSupervisor – activityIdleFromMessage

消息的执行也在 ActivityTaskSupervisor 类。

# ActivityTaskSupervisorprivate final class ActivityTaskSupervisorHandler extends Handler {@Overridepublic void handleMessage(Message msg) {synchronized (mService.mGlobalLock) {// 处理消息if (handleMessageInner(msg)) {return;}}......}private boolean handleMessageInner(Message msg) {......case IDLE_NOW_MSG: {if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);// 打印activityIdleFromMessage((ActivityRecord) msg.obj, false /* fromTimeout */);} break;......}}

处理消息的时候也有log打印,“msg.obj” 为null。

继续看处理的代码

# ActivityTaskSupervisorprivate void activityIdleFromMessage(ActivityRecord idleActivity, boolean fromTimeout) {activityIdleInternal(idleActivity, fromTimeout,fromTimeout /* processPausingActivities */, null /* config */);}void activityIdleInternal(ActivityRecord r, boolean fromTimeout,boolean processPausingActivities, Configuration config) {// 重点* 1 logif (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + r);// 重点* 2. ActivityRecord 不为null才执行内部逻辑if (r != null) {// 重点* 3. logif (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternal: Callers="+ Debug.getCallers(4));......}......// Atomically retrieve all of the other things to do. 原子检索所有其他要做的事情// 重点* 4. 看方法名是处理 停止和finish的Activity辑在这里processStoppingAndFinishingActivities(r, processPausingActivities, "idle");if (DEBUG_IDLE) {// 重点* 5. log Slogf.i(TAG, "activityIdleInternal(): r=%s, mStartingUsers=%s", r, mStartingUsers);}......}

这里有5个重点,其中1,3,5都是log,很少会将log标记为重点,当前这么做是因为这个方法会执行两次,需要通过这3个log来区分

现在是分析第一次执行,根据前面的分析参数 r 是null,所以执行 processStoppingAndFinishingActivities 方法。

log中搜索"Activity idle: "可以有以下2次打印

Line 5664: 04-01 20:50:30.411  8585  8751 V ActivityTaskManager: Activity idle: null
Line 19079: 04-01 20:50:31.171  8585  9527 V ActivityTaskManager: Activity idle: ActivityRecord{95d53d7 u0 com.google.android.dialer/.extensions.GoogleDialtactsActivity} t26}

2次打印的行数和时间有很大的差距,说明不是一个时机执行的,当前分析的是第一次执行,是由 SourceActivity 执行 activityPaused 的时候会触发 addToStopping 流程。这一次参数 r 为null。

第二次打印的 r 为 TargetActivity 具体执行流程下面会分析。

4.1 processStoppingAndFinishingActivities 逻辑(其实啥也没干)

# ActivityTaskSupervisor/*** Processes the activities to be stopped or destroyed. This should be called when the resumed* 处理要停止或销毁的Activity。这应该在resumed时调用* activities are idle or drawn.*/private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,boolean processPausingActivities, String reason) {// 准备要执行 Stop 的Activity 集合 ArrayList<ActivityRecord> readyToStopActivities = null;// 重点 * 1. 遍历mStoppingActivitiesfor (int i = mStoppingActivities.size() - 1; i >= 0; --i) {// 获取到ActivityRecord (当前分析场景就1个)final ActivityRecord s = mStoppingActivities.get(i);final boolean animating = s.isAnimating(TRANSITION | PARENTS,ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)|| s.inTransition();// 日志ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "+ "finishing=%s", s, s.nowVisible, animating, s.finishing);// 条件满足才执行if (!animating || mService.mShuttingDown) {......// 打印 准备stop的logProtoLog.v(WM_DEBUG_STATES, "Ready to stop: %s", s);if (readyToStopActivities == null) {readyToStopActivities = new ArrayList<>();}// 重点 * 2. 添加进集合readyToStopActivities.add(s);// 从集合中移除mStoppingActivities.remove(i);}}// 重点 * 3. 遍历readyToStopActivitiesfinal int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();for (int i = 0; i < numReadyStops; i++) {final ActivityRecord r = readyToStopActivities.get(i);// 检查该ActivityRecord对象是否在历史记录中。  if (r.isInHistory()) {// 如果该ActivityRecord对象正在结束(可能是用户或系统触发的结束操作)。if (r.finishing) {// TODO(b/137329632): Wait for idle of the right activity, not just any.r.destroyIfPossible(reason);} else {// 重点* 4. 如果ActivityRecord对象不在结束状态,则尝试停止它r.stopIfPossible();}}}......}

这个方法的逻辑还是很重要的,不过当前这一次执行进来,因为条件不满足,其实相当于啥也没干。

    1. 这里遍历的集合,在 ActivityRecord::addToStopping 方法中看到对其赋值
    1. 满足条件的会被添加进 readyToStopActivities 集合, 进入if的条件刚好log也有打印
V WindowManager: Stopping ActivityRecord{f40797d u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t20}: nowVisible=true animating=true finishing=false  mShuttingDown=false

animating 和 mShuttingDown 都不满足条件,所以不会进入。自如也不会有下面的"Ready to stop:"的打印,因为这里没进入执行 readyToStopActivities 集合没有元素,所以重点3也不会执行。

但是第二次执行 ActivityTaskSupervisor::activityIdleInternal 方法的时候的日志会有后续的打印,也就是说会执行到 ActivityRecord::stopIfPossible ,第二次的log如下

04-01 20:50:31.182  8585  9527 V WindowManager: Stopping ActivityRecord{f40797d u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t20}: nowVisible=false animating=false finishing=false  mShuttingDown=false
04-01 20:50:31.182  8585  9527 V WindowManager: Ready to stop: ActivityRecord{f40797d u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t20}

第二次相关的逻辑有个印象几个,后面会再分析

5 小结

在正确冷启动流程的开始 SourceActivity 执行 activityPaused 的时候会触发 addToStopping 流程,这个流程会执行到 ActivityTaskSupervisor:: processStoppingAndFinishingActivities 方法,但是由于条件不满足,在这个方法中不会有什么实质性的处理。(后续还有会流程第二次触发该方法)

因此 addToStopping 流程最重要的事就是将 SourceActivity 添加到了 ActivityTaskSupervisor 类下的 mStoppingActivities 集合中。等后面条件满足再触发 ActivityTaskSupervisor:: processStoppingAndFinishingActivities 方法时就会执行 ActivityRecord::stopIfPossible 流程了。

“wm_add_to_stopping” 日志的线索跟到这里就结束了,后面的流程还有2个线索

    1. 第二个和 stop 有关的events 日志为 “wm_stop_activity” 并且已知在 “wm_on_resume_called”后打印
    1. ActivityTaskSupervisor::activityIdleInternal 的第二次执行,并且第二次执行到 ActivityTaskSupervisor::processStoppingAndFinishingActivities 方法会触发后续逻辑创建 Stop 的事务。

相关文章:

【Android 源码分析】Activity生命周期之onStop-1

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…...

【Unity】本地化实现

个人向笔记。 1 前言 记录一下自己的本地化实现思路&#xff0c;暂时只讲本文的本地化实现。 2 文本本地化方案-个人 本地化实现是基于Luban的。自己使用Luban实现了一个“配置表模块”&#xff0c;又实现了一个“全局配置模块”&#xff0c;之后再基于这两个模块实现了“文本…...

Django一分钟:在Django中怎么存储树形结构的数据,DRF校验递归嵌套模型的替代方案

引言 在开发过程中我们可能需要这样的树形结构: [{"data": {"name": "牛奶"},"children": [{"data": {"name": "蒙牛"}, },{"data": {"name": "伊利"}, }]},{"da…...

【Docker从入门到进阶】06.常见问题与解决方案 07.总结与资源

6. 常见问题与解决方案 在使用Docker进行开发和部署过程中&#xff0c;可能会遇到各种问题。以下是一些常见问题及其解决方案&#xff1a; 容器启动失败和调试 在使用 Docker 时&#xff0c;容器启动失败或立即退出可能会导致一定的困扰&#xff0c;以下是进一步深入解决该问…...

快速排序的非递归实现:借助栈实现、借助队列实现

目录 用栈实现快速排序 1.用栈实现非递归快速排序的思路步骤 1.1.思路步骤 2.用栈实现非递归快速排序的代码 3.用栈实现非递归快速排序的整个工程 3.1.QuickSortNonR.h 3.2.QuickSortNonR.c 3.3.Stack.h 3.4.Stack.c 用队列实现非递归快速排序 1.用队列实现非递归快…...

Finops成本优化企业实践-可视化篇

引言&#xff1a;上一章讨论了finops的一些方法论&#xff0c;笔者在拿到finops官方认证finops-engineer certificate之后&#xff0c;将方法论运用到所在项目组中&#xff0c;并于今年完成了40%的费用节省。在此将这些实践方法总结沉淀&#xff0c;与大家分享。实践包括三篇&a…...

Spring Boot中线程池使用

说明&#xff1a;在一些场景&#xff0c;如导入数据&#xff0c;批量插入数据库&#xff0c;使用常规方法&#xff0c;需要等待较长时间&#xff0c;而使用线程池可以提高效率。本文介绍如何在Spring Boot中使用线程池来批量插入数据。 搭建环境 首先&#xff0c;创建一个Spr…...

Python机器学习:自然语言处理、计算机视觉与强化学习

&#x1f4d8; Python机器学习&#xff1a;自然语言处理、计算机视觉与强化学习 目录 ✨ 自然语言处理&#xff08;NLP&#xff09; 文本预处理&#xff1a;分词、去停用词词向量与文本分类&#xff1a;使用Word2Vec与BERT &#x1f306; 计算机视觉基础 图像预处理与增强目标…...

Vue2 + ElementUI + axios + VueRouter入门

之前没有pc端开发基础&#xff0c;工作需要使用若依框架进行了一年的前端开发.最近看到一个视频框架一步步集成&#xff0c;感觉颇受启发&#xff0c;在此记录一下学习心得。视频链接:vue2element ui 快速入门 环境搭建和依赖安装 安装nodejs安装Vue Cli使用vue create proje…...

GO网络编程(四):海量用户通信系统2:登录功能核心【重难点】

目录 一、C/S详细通信流程图二、消息类型定义与json标签1. 消息类型定义2. JSON标签3.结构体示例及其 JSON 表示&#xff1a;4.完整代码与使用说明 三、客户端发送消息1. 连接到服务器2. 准备发送消息3. 创建 LoginMes 并序列化4. 将序列化后的数据嵌入消息结构5. 序列化整个 M…...

某项目实战分析代码二

某项目实战分析代码二 此次分析的是protobuf的使用操作流程具体实现 3. 业务数据分析3.1 客户端3.2 服务器端简单案例 此次分析的是protobuf的使用 Protocol Buffer( 简称 Protobuf) 是Google公司内部的混合语言数据标准&#xff0c;它是一种轻便高效的结构化数据存储格式&…...

全面指南:探索并实施解决Windows系统中“mfc140u.dll丢失”的解决方法

当你的电脑出现mfc140u.dll丢失的问题是什么情况呢&#xff1f;mfc140u.dll文件依赖了什么&#xff1f;mfc140u.dll丢失会导致电脑出现什么情况&#xff1f;今天这篇文章就和大家聊聊mfc140u.dll丢失的解决办法。希望能够有效的帮助你解决这问题。 哪些程序依赖mfc140u.dll文件…...

QT学习笔记1(QT和QT creator介绍)

QT学习笔记1&#xff08;QT和QT creator介绍&#xff09; Qt 是一个跨平台的应用开发框架&#xff0c;主要用于图形用户界面&#xff08;GUI&#xff09;应用的开发&#xff0c;但也支持非GUI程序的开发。Qt 支持多种平台&#xff0c;如Windows、macOS、Linux、iOS和Android&a…...

存储电话号码的数据类型,用 int 还是用 string?

在 Java 编程中&#xff0c;存储电话号码的选择可以通过两种常见方式进行&#xff1a;使用 int 类型或 String 类型。这种选择看似简单&#xff0c;但实际上涉及到 JVM 内部的字节码实现、内存优化、数据表示、以及潜在的可扩展性问题。 Java 基本数据类型与引用数据类型的差异…...

【目标检测】工程机械车辆数据集2690张4类VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2694 标注数量(xml文件个数)&#xff1a;2694 标注数量(txt文件个数)&#xff1a;2694 标注…...

target_link_libraries()

target_link_libraries() 是 CMake 中的一个命令&#xff0c;用于指定目标&#xff08;如可执行文件或库&#xff09;所依赖的其他库。其主要作用包括&#xff1a; 链接库&#xff1a;将指定的库链接到目标上&#xff0c;使目标能够调用这些库中的函数和使用其功能。 管理依赖…...

Javascript数组研究09_Array.prototype[Symbol.unscopables]

Symbol.unscopables 是 JavaScript 中一个相对较新的符号&#xff08;Symbol&#xff09;&#xff0c;用于控制对象属性在 with 语句中的可见性。它主要用于内置对象&#xff0c;如 Array.prototype&#xff0c;以防止某些方法被引入到 with 语句的作用域中&#xff0c;避免潜在…...

SkyWalking 自定义链路追踪

对项目中的业务方法&#xff0c;实现链路追踪&#xff0c;方便我们排查问题 引入依赖 <!‐‐ SkyWalking 工具类 ‐‐> <dependency> <groupId>org.apache.skywalking</groupId> <artifactId>apm‐toolkit‐trace</artifactId> <vers…...

Linux驱动开发(速记版)--设备模型

第八十章 设备模型基本框架-kobject 和 kset 80.1 什么是设备模型 设备模型使Linux内核处理复杂设备更高效。 字符设备驱动适用于简单设备&#xff0c;但对于电源管理和热插拔&#xff0c;不够灵活。 设备模型允许开发人员以高级方式描述硬件及关系&#xff0c;提供API处理设备…...

动手学深度学习(李沐)PyTorch 第 6 章 卷积神经网络

李宏毅-卷积神经网络CNN 如果使用全连接层&#xff1a;第一层的weight就有3*10^7个 观察 1&#xff1a;检测模式不需要整张图像 很多重要的pattern只要看小范围即可 简化1&#xff1a;感受野 根据观察1 可以做第1个简化&#xff0c;卷积神经网络会设定一个区域&#xff0c…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

aurora与pcie的数据高速传输

设备&#xff1a;zynq7100&#xff1b; 开发环境&#xff1a;window&#xff1b; vivado版本&#xff1a;2021.1&#xff1b; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程&#xff0c;pc通过pcie传输给fpga&#xff0c;fpga再通过aur…...

Shell 解释器​​ bash 和 dash 区别

bash 和 dash 都是 Unix/Linux 系统中的 ​​Shell 解释器​​&#xff0c;但它们在功能、语法和性能上有显著区别。以下是它们的详细对比&#xff1a; ​​1. 基本区别​​ ​​特性​​​​bash (Bourne-Again SHell)​​​​dash (Debian Almquist SHell)​​​​来源​​G…...

OCC笔记:TDF_Label中有多个相同类型属性

注&#xff1a;OCCT版本&#xff1a;7.9.1 TDF_Label中有多个相同类型的属性的方案 OCAF imposes the restriction that only one attribute type may be allocated to one label. It is necessary to take into account the design of the application data tree. For exampl…...