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

Android Framework AMS(06)startActivity分析-3(补充:onPause和onStop相关流程解读)

该系列文章总纲链接:专题总纲目录 Android Framework 总纲


本章关键点总结 & 说明:

说明:本章节主要解读AMS通过startActivity启动Activity的整个流程的补充,更新了startActivity流程分析部分。

一般来说,有Activity启动,就有Activity退到后台。那么后者这个操作具体是怎样的呢?我们以startPausingLocked方法和stopActivityLocked方法为入口分别对到activity的onPause和AMS到onStop流程进行分析。

该部分是AMS.startActivity运行中生命周期的补充,毕竟startActivity关注onCreate、onStart、onResume ,但activity的生命周期还涉及onPause、onStop、onDestroy。本章主要介绍onPause、onStop。最后以onStop为例介绍了下APP中回调超时的设计理念。

1 startPausingLocked方法解读(onPause处理)

在之前AMS.startActivity的流程分析中执行resume相关操作时就已经开始执行startPausingLocked方法了。startPausingLocked的调用入口是resume相关函数,代码如下所示:

//ActivityStack//关键流程:step1final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {// 检查是否已经在执行恢复顶部 Activity 的操作,防止递归调用if (mStackSupervisor.inResumeTopActivity) {return false;   // 如果已经在执行恢复操作,则直接返回 false}// 初始化结果变量,用于记录恢复操作是否成功boolean result = false;try {// 设置一个标志,表示正在执行恢复顶部 Activity 的操作,防止递归mStackSupervisor.inResumeTopActivity = true;//锁屏状态相关处理//...//关键方法:调用内部方法实际执行恢复顶部 Activity 的操作result = resumeTopActivityInnerLocked(prev, options);} finally {// 恢复完成后,重置正在执行恢复操作的标志mStackSupervisor.inResumeTopActivity = false;}// 返回恢复操作的结果return result;}//关键流程:step2final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");//... boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);if (mResumedActivity != null) {pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);}if (pausing) {if (next.app != null && next.app.thread != null) {mService.updateLruProcessLocked(next.app, true, null);}return true;}//...return true;}

这里调用了startPausingLocked方法,当新启动一个activity时,系统将先处理当前的activity,即调用该方法来pause当前activity,代码实现如下:

//ActivityStackfinal boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,boolean dontWait) {// 如果已经在暂停一个 Activity,完成暂停操作。if (mPausingActivity != null) {completePauseLocked(false);}// 记录当前处于前台的 Activity。ActivityRecord prev = mResumedActivity;// 如果没有前台 Activity,且不是正在恢复状态,尝试恢复顶层 Activity。if (prev == null) {if (!resuming) {mStackSupervisor.resumeTopActivitiesLocked();}return false; // 如果没有前台 Activity,返回 false。}// 如果当前 Activity 没有父 Activity,暂停所有子栈。if (mActivityContainer.mParentActivity == null) {mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);}// 将当前前台 Activity 设置为 null,准备暂停。mResumedActivity = null;// 标记 prev 为正在暂停的 Activity。mPausingActivity = prev;// 记录最后一个暂停的 Activity。mLastPausedActivity = prev;// 确定最后一个没有历史记录的 Activity。mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0|| (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;// 更新 prev Activity 的状态为 PAUSING。prev.state = ActivityState.PAUSING;// 更新任务的活动时间。prev.task.touchActiveTime();// 清除启动时间。clearLaunchTime(prev);// 获取顶层运行的 Activity。final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();// 如果支持最近任务并且下一个 Activity 为空或者不显示或者不在同一个任务或者 UI 休眠,则更新缩略图。if (mService.mHasRecents && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {prev.updateThumbnailLocked(screenshotActivities(prev), null);}// 停止完全绘制的跟踪(可能是性能监控的一部分)。stopFullyDrawnTraceIfNeeded();// 更新 CPU 统计信息。mService.updateCpuStats();// 如果 prev 的应用和线程不为空,发送暂停 Activity 的请求。if (prev.app != null && prev.app.thread != null) {try {mService.updateUsageStats(prev, false);// 关键方法:调用当前Activity所在进程的schedulePauseActivity方法prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,userLeaving, prev.configChangeFlags, dontWait);} catch (Exception e) {// 异常处理(代码省略)。}} else {// 如果应用或线程为空,清除暂停的 Activity 记录。mPausingActivity = null;mLastPausedActivity = null;mLastNoHistoryActivity = null;}// 如果系统不是在休眠或关闭状态,获取启动锁。if (!mService.isSleepingOrShuttingDown()) {mStackSupervisor.acquireLaunchWakelock();}// 如果正在暂停一个 Activity,处理是否等待暂停完成。if (mPausingActivity != null) {if (!uiSleeping) {// 如果 UI 不在休眠状态,暂停按键分发。prev.pauseKeyDispatchingLocked();}if (dontWait) {// 如果不需要等待,完成暂停并返回 false。completePauseLocked(false);return false;} else {// 如果需要等待,发送暂停超时的消息,并设置超时时间。Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);msg.obj = prev;prev.pauseTime = SystemClock.uptimeMillis();mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);return true;}} else {// 如果没有 Activity 正在暂停,尝试恢复顶层 Activity。if (!resuming) {mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);}return false;}}

startPausingLocked 方法用于处理 Activity 的暂停逻辑。它首先检查是否有正在进行的暂停操作,如果有,则完成它。然后,它检查是否有前台 Activity,如果没有,则尝试恢复顶层 Activity。如果有前台 Activity,它会更新一些状态和变量,准备暂停操作。如果系统支持最近任务并且条件满足,它会更新 Activity 的缩略图。然后注意:关键点来了,它发送一个暂停 Activity 的请求到应用线程。如果系统不是在休眠或关闭状态,它会获取一个启动锁。最后,根据是否需要等待暂停完成,它要么立即返回,要么设置一个超时消息来处理暂停超时情况。这个方法返回一个布尔值,表示是否成功启动了暂停操作。

接下来关注schedulePauseActivity方法实现,主要是通过消息处理最后调用handlePauseActivity方法,过程中的代码实现如下:

//ActivityThread//ApplicationThreadpublic final void schedulePauseActivity(IBinder token, boolean finished,boolean userLeaving, int configChanges, boolean dontReport) {sendMessage(finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,token,(userLeaving ? 1 : 0) | (dontReport ? 2 : 0),configChanges);}//...//Handler消息处理private class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {//...case PAUSE_ACTIVITY:handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,(msg.arg1&2) != 0);maybeSnapshot();break;//...}}//...}//...private void handlePauseActivity(IBinder token, boolean finished,boolean userLeaving, int configChanges, boolean dontReport) {// 根据 token 查找对应的 ActivityClientRecord 对象。ActivityClientRecord r = mActivities.get(token);// 如果找到了对应的 ActivityClientRecord。if (r != null) {if (userLeaving) { // 如果用户正在离开 Activity,执行相关的处理。performUserLeavingActivity(r);}r.activity.mConfigChangeFlags |= configChanges;// 关键方法:执行暂停 Activity 的操作。performPauseActivity(token, finished, r.isPreHoneycomb());// 如果应用的 target SDK 版本低于 Honeycomb(3.0),等待所有队列工作完成。if (r.isPreHoneycomb()) {QueuedWork.waitToFinish();}// 如果不应该报告 Activity 暂停,跳过报告步骤。if (!dontReport) {try {// 关键方法:向 ActivityManager 报告 Activity 已暂停。ActivityManagerNative.getDefault().activityPaused(token);} catch (RemoteException ex) {//...}}mSomeActivitiesChanged = true;}}

这里开始我们关注关键方法performPauseActivity和通知AMS做其他处理的操作。详细解读如下。

1.1 performPauseActivity方法解读

接下来,一条路线是为了启动activity并调用activity的onPause方法,关键方法为:

//ActivityThread//关键流程:step1final Bundle performPauseActivity(IBinder token, boolean finished,boolean saveState) {ActivityClientRecord r = mActivities.get(token);return r != null ? performPauseActivity(r, finished, saveState) : null;}//关键流程:step2final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,boolean saveState) {try {// 如果 Activity 没有完成(即没有调用过 finish()),并且需要保存状态,则调用 onSaveInstanceState()。if (!r.activity.mFinished && saveState) {callCallActivityOnSaveInstanceState(r);}// 标记 Activity 为已调用 onPause() 状态。r.activity.mCalled = false;// 调用 Activity 的 onPause() 方法。mInstrumentation.callActivityOnPause(r.activity);} catch (SuperNotCalledException e) {//...}// 标记 Activity 为已暂停状态。r.paused = true;// 获取所有注册的 onPaused 监听器,这些监听器将在 Activity 暂停时被通知。ArrayList<OnActivityPausedListener> listeners;synchronized (mOnPauseListeners) {listeners = mOnPauseListeners.remove(r.activity);}// 获取监听器列表的大小。int size = (listeners != null ? listeners.size() : 0);// 遍历所有监听器,调用它们的 onPaused() 方法。for (int i = 0; i < size; i++) {listeners.get(i).onPaused(r.activity);}// 如果 Activity 没有完成并且需要保存状态,则返回保存的状态 Bundle;否则返回 null。return !r.activity.mFinished && saveState ? r.state : null;}

这里较为简单,主要就是调用activty的onPause方法了(mInstrumentation.callActivityOnPause)。另一条路线是调用完通知AMS。接下来看看通知AMS都干了些啥。

1.2 通知AMS处理Pause的相关操作

在handlePauseActivity中最后部分执ActivityManagerNative.getDefault().activityPaused(token);调用到activityPaused方法,代码实现如下:

//ActivityManagerService//...@Overridepublic final void activityPaused(IBinder token) {final long origId = Binder.clearCallingIdentity();synchronized(this) {// 根据提供的 token 查找对应的 ActivityStack。ActivityStack stack = ActivityRecord.getStackLocked(token);if (stack != null) {// 关键方法:传递 token 和 false。stack.activityPausedLocked(token, false);}}Binder.restoreCallingIdentity(origId);}

这里主要关注activityPausedLocked方法的实现,代码实现如下:

//ActivityStack//关键流程:step1final void activityPausedLocked(IBinder token, boolean timeout) {// 根据提供的 token 查找对应的 ActivityRecordfinal ActivityRecord r = isInStackLocked(token);if (r != null) {// 从消息队列中移除任何与 PAUSE_TIMEOUT_MSG 相关的消息,避免暂停超时mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);// 如果当前正在暂停的 Activity 与找到的 ActivityRecord 相匹配if (mPausingActivity == r) {// 关键方法:完成暂停操作,传递 truecompletePauseLocked(true);}//...}}//关键流程:step2private void completePauseLocked(boolean resumeNext) {// 获取当前正在暂停的 ActivityRecordActivityRecord prev = mPausingActivity;if (prev != null) {// 将 Activity 的状态设置为 PAUSEDprev.state = ActivityState.PAUSED;// 如果 Activity 正在结束,则调用 finishCurrentActivityLocked 方法完成结束操作if (prev.finishing) {prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);} else if (prev.app != null) { // 如果 Activity 关联的应用不为空// 如果 Activity 正在等待可见状态,则更新状态并从等待列表中移除if (prev.waitingVisible) {prev.waitingVisible = false;mStackSupervisor.mWaitingVisibleActivities.remove(prev);}// 如果需要销毁 Activity,则调用 destroyActivityLocked 方法if (prev.configDestroy) {destroyActivityLocked(prev, true, "pause-config");} else if (!hasVisibleBehindActivity()) { // 如果没有可见的后置 Activity// 将当前 Activity 添加到停止的 Activity 列表中mStackSupervisor.mStoppingActivities.add(prev);// 根据条件决定是否调度空闲状态if (mStackSupervisor.mStoppingActivities.size() > 3 ||prev.frontOfTask && mTaskHistory.size() <= 1) {mStackSupervisor.scheduleIdleLocked();} else {mStackSupervisor.checkReadyForSleepLocked();}}} else {prev = null;}// 清空正在暂停的 ActivitymPausingActivity = null;}// 如果需要恢复下一个 Activityif (resumeNext) {final ActivityStack topStack = mStackSupervisor.getFocusedStack();// 如果服务没有进入休眠或关闭状态,恢复顶层 Activityif (!mService.isSleepingOrShuttingDown()) {mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);} else {// 检查是否准备好进入休眠状态mStackSupervisor.checkReadyForSleepLocked();ActivityRecord top = topStack.topRunningActivityLocked(null);// 如果顶层 Activity 为空或与当前暂停的 Activity 不同,恢复顶层 Activityif (top == null || (prev != null && top != prev)) {mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);}}}// 如果 prev 不为空,恢复按键分发if (prev != null) {prev.resumeKeyDispatchingLocked();//电池信息统计}// 通知任务栈发生了变化mService.notifyTaskStackChangedLocked();}

completePauseLocked 方法用于完成 Activity 的pause操作。它首先更新 Activity 的状态并处理与应用相关的各种情况,包括销毁、停止和恢复下一个 Activity。方法还处理 CPU 使用情况的更新,并在 Activity 状态变化时通知系统。这个方法确保了 Activity 在暂停时能够正确地保存状态和资源,同时为下一个 Activity 的恢复做好准备。

但是注意,这里涉及老activity的pause和新activity的resume操作,相信第一次看的伙伴并不容易理解,因此这里使用一个案例来帮忙理解。

1.3 场景案例解读completePauseLocked

这里使用一个场景案例来解读所谓的completePauseLocked在逻辑上到底是干了啥?便于理解。当使用 am 命令从 Launcher 界面启动一个 Settings Activity 时,Android 系统会经历以下步骤。每个步骤详细解读如下。

1.3.1 启动 Settings Activity:

系统通过 ActivityManagerService 的 startActivity() 方法启动新的 Settings Activity。

ActivityManagerService 负责协调启动过程,包括创建新的 Activity 记录(ActivityRecord)和调度启动。

1.3.2 Launcher 的 onPause() 调用:

在新的 Settings Activity 变得可见之前,当前的 Launcher Activity 需要进入暂停(paused)状态。

ActivityManagerService 会通知 Launcher Activity 调用它的 onPause() 生命周期方法。

这是通过 completePauseLocked() 方法完成的,该方法会更新 Launcher Activity 的状态为 PAUSED 并执行相关的暂停操作。

1.3.3 Settings Activity 的 onCreate() 和 onStart() 调用:

随着新的 Settings Activity 被创建,它的 onCreate() 和 onStart() 生命周期方法会被调用。

这些方法负责初始化 Activity,设置 UI,以及准备显示内容。

1.3.4 Settings Activity 的 onResume() 调用:

一旦 Settings Activity 准备好显示,它的 onResume() 方法会被调用。

onResume() 方法标志着 Activity 变得可见并开始与用户交互。

1.3.5 completePauseLocked() 中的逻辑:

如果 completePauseLocked() 方法被调用,它会处理 Launcher Activity 的暂停逻辑。

这包括调用 Launcher Activity 的 onPause() 方法,更新系统状态,以及准备恢复下一个 Activity。

如果 resumeNext 参数为 true,completePauseLocked() 方法会请求 ActivityManagerService 恢复下一个 Activity,即 Settings Activity。

1.3.6 Settings Activity 的恢复:

在 Launcher Activity 暂停后,ActivityManagerService 会负责恢复 Settings Activity。

这涉及到调用 Settings Activity 的 onResume() 方法,使其成为前台 Activity。

总结来说,在这种场景下,completePauseLocked() 方法主要负责处理 Launcher Activity 的暂停逻辑,而 Settings Activity 的 onResume() 方法会在 Launcher Activity 暂停后被调用,以确保用户体验的连续性和响应性。这个过程是由 ActivityManagerService 协调。

2 stopActivityLocked方法(onStop处理)

2.1 找到stopActivityLocked调用的入口Idler

stopActivityLocked藏的比较深,接下来我们从handleResumeActivity开始。想更多了解可参考文

章:Android Framework AMS(05)startActivity分析-2(ActivityThread启动到Activity拉起),文中

2.2.2 对handleResumeActivity方法有详细的解读,本次我们关注点不同,handleResumeActivity

关键代码解读如下:

//ActivityThreadfinal void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {unscheduleGcIdler();  // 取消垃圾回收的定时器,准备恢复 ActivitymSomeActivitiesChanged = true;  // 标记有 Activity 状态改变// 关键方法:resume Activity 的操作ActivityClientRecord r = performResumeActivity(token, clearHide);  if (r != null) {//...if (!r.onlyLocalRequest) {r.nextIdle = mNewActivities;  // 设置下一个空闲的 ActivitymNewActivities = r;  // 设置新的 ActivityLooper.myQueue().addIdleHandler(new Idler());  // 添加空闲处理器}r.onlyLocalRequest = false;  // 清除本地请求标志if (reallyResume) {try {ActivityManagerNative.getDefault().activityResumed(token);  // 通知 AMS Activity 已resume} catch (RemoteException ex) {}}} else {//...}}

这里我们主要关注一句话:

Looper.myQueue().addIdleHandler(new Idler());

接下来我们对这句话进行详细解读:

  • Looper.myQueue():获取当前线程的消息队列。在 Android 中,每个线程都有自己的消息队列,而主线程的消息队列是处理 UI 更新和生命周期回调的核心。Looper 对象与消息队列关联,负责循环处理队列中的消息。
  • addIdleHandler():向消息队列添加一个空闲处理器(IdleHandler)。空闲处理器是一种特殊的回调接口,它在消息队列中没有消息时被调用,即在消息队列空闲时触发。
  • new Idler():创建一个新的 Idler 实例。Idler 是 IdleHandler 接口的一个实现,它定义了在消息队列空闲时执行的操作。

这行代码的设计目的是为了在主线程空闲时进行垃圾回收和其他优化操作。通过这种方式,系统可以在不影响 UI 性能的情况下,做一些事情,比如:提高资源利用率,减少内存泄漏,优化电池使用,并提供更平滑的用户体验。这是 Android 系统中一个重要的性能优化机制。

当消息队列中没有消息时Idler 会被调用,更具体地说,IdleHandler 的 queueIdle 方法将会被调用。接下里我们看该方法的实现,代码如下:

//ActivityThread//Idler@Overridepublic final boolean queueIdle() {//activity列表含义,最近被启动但还没有报告为空闲(idle)的 Activity 实例ActivityClientRecord a = mNewActivities;boolean stopProfiling = false;//性能相关处理//...if (a != null) { // 如果存在新的 Activity 记录// 清空 mNewActivities 链表,准备处理这些 ActivitymNewActivities = null;IActivityManager am = ActivityManagerNative.getDefault();ActivityClientRecord prev;do {// 如果 Activity 存在且没有finishif (a.activity != null && !a.activity.mFinished) {try {// 通知 AMS,Activity 已经空闲am.activityIdle(a.token, a.createdConfig, stopProfiling);// 清空创建配置,表示已经处理a.createdConfig = null;} catch (RemoteException ex) {//...}}// 记录当前 Activity 记录,然后移动到下一个prev = a;a = a.nextIdle;// 断开当前记录的下一个引用,避免内存泄漏prev.nextIdle = null;} while (a != null);}//...ensureJitEnabled();// 返回false 表示不再需要这个IdleHandler,将其从消息队列中移除return false;}

这个方法主要用于处理消息队列空闲时的一些操作,这组 ActivityClientRecord 列表代表了那些需要进一步处理或状态更新的 Activity 实例。它们在消息队列空闲时被处理,通知 AMS 某个 Activity 已经空闲。这些操作都是在 UI 线程没有其他工作时完成的,这样可以确保不会影响用户界面的响应性。activityIdle代码实现如下:

//ActivityManagerService@Overridepublic 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, config);//性能相关处理//...}}Binder.restoreCallingIdentity(origId);}

继续分析ActivityStackSupervisor.activityIdleInternalLocked方法的实现,代码如下:

//ActivityStackSupervisorfinal ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,Configuration config) {ArrayList<ActivityRecord> stops = null;ArrayList<ActivityRecord> finishes = null;ArrayList<UserStartedState> startingUsers = null;int NS = 0; // 停止activity的计数int NF = 0; // 结束activity的计数boolean booting = false; // 标记系统是否正在启动boolean activityRemoved = false; // 标记是否有activity被移除// 根据 token 获取对应的 ActivityRecord 对象ActivityRecord r = ActivityRecord.forToken(token);if (r != null) {mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);r.finishLaunchTickingLocked();// 如果是因为超时导致的空闲,报告activity已启动// 针对stop的分析,这里fromTimeout=falseif (fromTimeout) {reportActivityLaunchedLocked(fromTimeout, r, -1, -1);}if (config != null) {r.configuration = config;}r.idle = true;// 标记 Activity 为空闲状态// 如果 Activity 位于前台栈或者是因为超时,检查是否完成启动if (isFrontStack(r.task.stack) || fromTimeout) {booting = checkFinishBootingLocked();}}// 如果所有恢复的activity都处于空闲状态,调度应用程序的垃圾回收if (allResumedActivitiesIdle()) {if (r != null) {mService.scheduleAppGcsLocked();}// 如果有activity正在启动并且被暂停,移除相关的消息并释放if (mLaunchingActivity.isHeld()) {mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);//...mLaunchingActivity.release();}// 确保activity可见ensureActivitiesVisibleLocked(null, 0);}// 处理所有标记为停止的activitystops = processStoppingActivitiesLocked(true);NS = stops != null ? stops.size() : 0;// 如果有等待完成的activity,将它们添加到列表中并清空原列表if ((NF=mFinishingActivities.size()) > 0) {finishes = new ArrayList<ActivityRecord>(mFinishingActivities);mFinishingActivities.clear();}// 如果有正在启动的用户,将它们添加到列表中并清空原列表if (mStartingUsers.size() > 0) {startingUsers = new ArrayList<UserStartedState>(mStartingUsers);mStartingUsers.clear();}// 停止所有标记为停止的activityfor (int i = 0; i < NS; i++) {r = stops.get(i);final ActivityStack stack = r.task.stack;if (r.finishing) {// 如果activity正在完成,立即结束它stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);} else {//关键方法:stop activity操作stack.stopActivityLocked(r);}}// 完成所有标记为结束的activityfor (int i = 0; i < NF; i++) {r = finishes.get(i);// 如果activity被移除,标记 activityRemoved 为 trueactivityRemoved |= r.task.stack.destroyActivityLocked(r, true, "finish-idle");}// 如果系统不在启动过程中,完成用户启动if (!booting) {if (startingUsers != null) {for (int i = 0; i < startingUsers.size(); i++) {mService.finishUserSwitch(startingUsers.get(i));}}if (mStartingBackgroundUsers.size() > 0) {startingUsers = new ArrayList<UserStartedState>(mStartingBackgroundUsers);mStartingBackgroundUsers.clear();for (int i = 0; i < startingUsers.size(); i++) {mService.finishUserBoot(startingUsers.get(i));}}}// 修剪应用程序,释放无用资源mService.trimApplications();// 如果有activity被移除,恢复顶层activityif (activityRemoved) {resumeTopActivitiesLocked();}return r;}

至此,终于看到stopActivityLocked操作了。接下来我们以分析此方法为主。

2.2 stopActivityLocked方法解读(onStop处理)

stopActivityLocked方法的实现,代码如下:

//ActivityStackfinal void stopActivityLocked(ActivityRecord r) {// 如果 Activity 在其 intent 或 activity info 中被标记为没有历史记录(即不在后退栈中出现)if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0|| (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {// 如果 Activity 还没有结束if (!r.finishing) {// 如果系统没有在睡眠状态if (!mService.isSleeping()) {// 请求结束这个 Activity,并传递一个取消的结果码requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, "no-history", false);}}}// 如果 Activity 的应用和线程不为空if (r.app != null && r.app.thread != null) {// 调整焦点 ActivityadjustFocusedActivityLocked(r, "stopActivity");// 恢复分派按键事件r.resumeKeyDispatchingLocked();try {// 重置 Activity 的 stopped 状态r.stopped = false;// 更新 Activity 的状态为 STOPPINGr.state = ActivityState.STOPPING;// 如果 Activity 不可见,通知窗口管理器if (!r.visible) {mWindowManager.setAppVisibility(r.appToken, false);}// 关键方法:通过应用线程请求停止 Activityr.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);// 如果系统正在睡眠或关闭,设置 Activity 为睡眠状态if (mService.isSleepingOrShuttingDown()) {r.setSleeping(true);}// 获取一个消息对象,并将其发送到消息队列,如果 Activity 停止操作超时,将处理该消息Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);} catch (Exception e) {// 异常处理(省略)}}}

stopActivityLocked 负责停止一个 Activity。它首先检查 Activity 是否被标记为没有历史记录,如果是,并且 Activity 还没有结束,且系统不在睡眠状态,它会请求结束这个 Activity。接下来,如果 Activity 的应用和线程不为空,它会调整焦点 Activity,恢复按键分派,更新 Activity 的状态为 STOPPING,并请求应用线程停止这个 Activity,这里较为关键。如果系统正在睡眠或关闭,它会设置 Activity 为睡眠状态。最后,它会发送一个延迟消息,用于处理停止操作的超时情况。

接下来我们关注关键方法ActivityThread.scheduleStopActivity的实现,代码如下:

//ActivityThread//ApplicationThreadpublic final void scheduleStopActivity(IBinder token, boolean showWindow,int configChanges) {sendMessage(showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,token, 0, configChanges);}//...//Handler消息处理private class H extends Handler {public void handleMessage(Message msg) {switch (msg.what) {//...case STOP_ACTIVITY_SHOW:handleStopActivity((IBinder)msg.obj, true, msg.arg2);break;//...}}//...}//...private void handleStopActivity(IBinder token, boolean show, int configChanges) {ActivityClientRecord r = mActivities.get(token);r.activity.mConfigChangeFlags |= configChanges;StopInfo info = new StopInfo();// 执行停止 Activity 的内部操作performStopActivityInner(r, info, show, true);// 更新 Activity 的可见性状态updateVisibility(r, show);//...// 安排在 UI 线程上告诉 Activity Manager 我们已经停止了 Activity// 我们不是立即这样做,因为我们希望在通知 Activity Manager 之前,// 有机会完成任何其他挂起的工作(特别是内存修剪请求),然后才能让应用完全进入后台info.activity = r;info.state = r.state;info.persistentState = r.persistentState;mH.post(info);mSomeActivitiesChanged = true; // 标记有 Activity 状态改变}

这里继续分析performStopActivityInner方法的实现,代码如下:

//ActivityThreadprivate void performStopActivityInner(ActivityClientRecord r,StopInfo info, boolean keepShown, boolean saveState) {if (r != null) {//...// 如果 Activity 没有完成(即没有调用过 finish()),并且需要保存状态if (!r.activity.mFinished && saveState) {// 如果 Activity 的状态对象为空,调用 onSaveInstanceState() 方法来保存状态if (r.state == null) {callCallActivityOnSaveInstanceState(r);}}// 如果 Activity 不需要保持显示状态if (!keepShown) {try {// 调用 Activity 的 performStop() 方法来执行停止操作r.activity.performStop();} catch (Exception e) {// ...}r.stopped = true;// 标记 Activity 为已停止状态}r.paused = true;// 标记 Activity 为已暂停状态}}

performStopActivityInner 负责执行 Activity 的停止操作。它首先检查 Activity 是否需要保持显示状态。如果不需要,并且 Activity 之前没有被停止过,它会执行停止操作。如果需要保存状态,它会调用 onSaveInstanceState() 方法来保存 Activity 的状态。最后,它调用 Activity 的performStop() 方法来执行实际的停止操作,并更新 Activity 的状态为已停止和已暂停。performStop方法实现,代码如下:

//Activityfinal void performStop() {// 标记不再需要报告完全绘制状态mDoReportFullyDrawn = false;// 如果加载器已经开始,现在需要停止它们if (mLoadersStarted) {mLoadersStarted = false; // 标记加载器已停止// 如果有加载器管理器,根据配置更改情况执行停止或保留操作if (mLoaderManager != null) {if (!mChangingConfigurations) {mLoaderManager.doStop(); // 停止加载器} else {mLoaderManager.doRetain(); // 保留加载器}}}// 如果 Activity 之前没有被停止过if (!mStopped) {// 如果有窗口对象,关闭所有面板if (mWindow != null) {mWindow.closeAllPanels();}// 如果 Activity 有 token 且没有父 Activity,通知窗口管理器该 Activity 已停止if (mToken != null && mParent == null) {WindowManagerGlobal.getInstance().setStoppedState(mToken, true);}// 分发 Fragment 的停止事件mFragments.dispatchStop();// 标记未调用 onStop()mCalled = false;//关键方法:该方法间接调用 Activity 的 onStop() 方法mInstrumentation.callActivityOnStop(this);//光标处理相关//...mStopped = true;// 标记 Activity 为已停止状态}mResumed = false;// 标记 Activity 不再处于 resume 状态}

performStop() 方法执行了 onStop() 生命周期方法之后的操作,包括停止 Loader,关闭窗口面板,更新 Fragment 的状态,释放数据库和光标资源,以及更新 Activity 的状态。这个方法确保了在 Activity 停止时,所有资源都被正确管理和释放,以避免内存泄漏和其他资源问题。此外,它还确保了 onStop() 方法被正确调用,如果没有,会抛出异常。最后更新了 Activity 的状态,以反映它不再处于 resume 状态。

至此,调用到activity的onStop方法了(mInstrumentation.callActivityOnStop)。有了onPause和onStop的分析方法,感兴趣的伙伴可以自行分析下onDestroy的流程。这里不再编写该部分。

3 超时设计的解读

这里的超时的设计以Activity的onStop超时处理为例。即2.2 中stopActivityLocked方法为例,解读下onStop的超时处理流程,整理后相关代码如下:

//ActivityStackfinal void stopActivityLocked(ActivityRecord r) {//...// 如果 Activity 的应用和线程不为空if (r.app != null && r.app.thread != null) {//...try {//...// 关键方法:通过应用线程请求停止 Activityr.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);//...// 获取一个消息对象,并将其发送到消息队列,如果 Activity 停止操作超时,将处理该消息Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);} catch (Exception e) {// 异常处理(省略)}}}

可以看到执行scheduleStopActivity后开始用handler发送延迟消息。注意:这里是不管是否延迟都会发消息。那么消息是在哪里处理的呢?

我们以scheduleStopActivity为入口进行分析。这里scheduleStopActivity通过handler发送消息,最终由handleStopActivity来处理,我们就从handleStopActivity这个方法开始分析,代码整理后,关键代码如下:

//ActivityThreadprivate void handleStopActivity(IBinder token, boolean show, int configChanges) {ActivityClientRecord r = mActivities.get(token);r.activity.mConfigChangeFlags |= configChanges;StopInfo info = new StopInfo();performStopActivityInner(r, info, show, true);//...updateVisibility(r, show);//...//这里开始通过post执行info的run方法info.activity = r;info.state = r.state;info.persistentState = r.persistentState;mH.post(info);mSomeActivitiesChanged = true;}

这里主要关注StopInfo的实现及run方法,因为post会导致run的调用,StopInfo代码如下:

    private static class StopInfo implements Runnable {ActivityClientRecord activity;Bundle state;PersistableBundle persistentState;CharSequence description;@Override public void run() {try {ActivityManagerNative.getDefault().activityStopped(activity.token, state, persistentState, description);} catch (RemoteException ex) {}}}

继续分析AMS的关键方法activityStopped的实现,代码如下:


//ActivityManagerService@Overridepublic final void activityStopped(IBinder token, Bundle icicle,PersistableBundle persistentState, CharSequence description) {//...final long origId = Binder.clearCallingIdentity();synchronized (this) {ActivityRecord r = ActivityRecord.isInStackLocked(token);if (r != null) {r.task.stack.activityStoppedLocked(r, icicle, persistentState, description);}}trimApplications();Binder.restoreCallingIdentity(origId);}

继续分析这里的关键方法activityStoppedLocked的实现,整理关键代码内容分析,代码如下:

//ActivityStackfinal void activityStoppedLocked(ActivityRecord r, Bundle icicle,PersistableBundle persistentState, CharSequence description) {if (r.state != ActivityState.STOPPING) {Slog.i(TAG, "Activity reported stop, but no longer stopping: " + r);mHandler.removeMessages(STOP_TIMEOUT_MSG, r);return;}//...}

如果一切正常,那么最后会通过AMS,在ActivityStack的activityStoppedLocked中将这个超时消息移除。也就是正常情况下,只要不超过这个超时的时间,都会正常运行;出现超时的异常情况会导致延迟消息未取消而正常发送,导致异常处理的流程。

以上是以onStop的流程未基础进行分析,其他的onCreate、onStart、onResume、onPause等也是按照类似方式来处理超时的。

相关文章:

Android Framework AMS(06)startActivity分析-3(补充:onPause和onStop相关流程解读)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读AMS通过startActivity启动Activity的整个流程的补充&#xff0c;更新了startActivity流程分析部分。 一般来说&#xff0c;有Activ…...

【LangChain系列2】【Model I/O详解】

目录 前言一、LangChain1-1、介绍1-2、LangChain抽象出来的核心模块1-3、特点1-4、langchain解决的一些行业痛点1-5、安装 二、Model I/O模块2-0、Model I/O模块概要2-1、Format&#xff08;Prompts Template&#xff09;2-1-1、Few-shot prompt templates2-1-2、Chat模型的少样…...

动态规划-子数组系列——1567.乘积为正数的最长子数组

1.题目解析 题目来源&#xff1a;1567.乘积为正数的最长子数组——力扣 测试用例 2.算法原理 1.状态表示 因为数组中存在正数与负数&#xff0c;如果求乘积为正数的最长子数组&#xff0c;那么存在两种情况使得乘积为正数&#xff0c;第一种就是正数乘以正数&#xff0c;第…...

Linux 运行执行文件并将日志输出保存到文本文件中

在 Linux 系统中运行可执行文件并将日志输出保存到文本文件中&#xff0c;可以使用以下几种方法&#xff1a; 方法一&#xff1a;使用重定向符号 > 或 >> 覆盖写入&#xff08;>&#xff09;&#xff1a; ./your_executable > logfile.txt这会将可执行文件的输…...

注册安全分析报告:北外网校

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…...

预警期刊命运逆袭到毕业好刊,仅45天!闭眼冲速度,发文量暴增!

选刊发表不迷路&#xff0c;就找科检易学术 期刊官网&#xff1a;Sustainability | An Open Access Journal from MDPI 1、期刊信息 期刊简介&#xff1a; Sustainability 是一本国际性的、同行评审的开放获取期刊&#xff0c;由MDPI出版社每半月在线出版。该期刊专注于人类…...

【LeetCode每日一题】——523.连续的子数组和

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 前缀和 二【题目难度】 中等 三【题目编号】 523.连续的子数组和 四【题目描述】 给你一个…...

leetcode54:螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;matrix [[1,2,3,…...

全方面熟悉Maven项目管理工具(三)认识mvn的各类构建命令并创建、打包Web工程

1. POM&#xff08;核心概念&#xff09; 1.1 含义 POM&#xff1a; Project Object Model&#xff0c;项目对象模型。 DOM&#xff1a; Document Object Model&#xff0c;文档对象模型&#xff0c;和 POM 类似 它们都是模型化思想的具体体现 1.2 模型化思想 POM 表示将…...

MySQL中查询语句的执行流程

文章目录 前言流程图概述最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;今天我们一起探讨一下执行一条查询的SQL语句在MySQL内部都发生了什么&#xff0c;让你对MySQL内部的架构具备一个宏观上的了解 流程图 概述 对于查询语句的SQL的执行流程&#xff0c;主要可以分为…...

【代码随想录Day47】单调栈Part02

42. 接雨水 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;单调栈&#xff0c;经典来袭&#xff01;LeetCode:42.接雨水_哔哩哔哩_bilibili 思路概述 问题理解&#xff1a;我们需要计算在给定柱子高度之间可以接住的雨水总量。雨水的量取决于柱子的高度和它们…...

Java全栈经典面试题剖析3】JavaSE面向对象2

目录 面试题2.12 Overload和Override的区别 面试题2.13 Overload方法是否可以改变返回值的类型&#xff1f; 面试题2.14 为什么方法不能根据返回类型来区分重载&#xff1f; 面试题2.15 构造器可不可以被重载或重写&#xff1f; 面试题2.16 在 Java 中定义⼀个不做事且没有…...

@JsonIgnoreProperties做接口对接时使用带来的好处

最近看到有个同事&#xff0c;在代码里面加了JsonIgnoreProperties这个注解&#xff0c;以前还真没有经常去用过&#xff0c;接口对接尤其是跟金蝶、用友等第三方&#xff0c;这个注解在接收数据是非常好用的&#xff1b;接下来带大家一起了解下具体的特性和使用方式 JsonIgno…...

SpringBoot整合mybatisPlus实现批量插入并获取ID

背景&#xff1a;需要实现批量插入并且得到插入后的ID。 使用for循环进行insert这里就不说了&#xff0c;在海量数据下其性能是最慢的。数据量小的情况下&#xff0c;没什么区别。 【1】saveBatch(一万条数据总耗时&#xff1a;2478ms) mybatisplus扩展包提供的&#xff1a;…...

实战RAG第一天——llama_index向量索引,查询引擎,搜索知识库问答,全部代码,保姆级教学

一、llama_index简介 llama_index(以前称为 GPT Index)是一个用于构建、查询、索引大型文档和数据集的开源框架。它的核心功能是帮助开发者将大语言模型(LLM)与自己的数据集无缝集成,从而进行知识库的构建、查询等任务。llama_index 使用 Python 编写,并结合了多种大语言…...

大数据治理

大数据治理是指对大数据的管理和控制,以确保数据的质量、可用性、安全性和合规性。随着大数据技术的不断发展,企业和组织面临着越来越多的数据管理挑战,如数据质量问题、数据安全问题、数据合规问题等。大数据治理成为了企业和组织应对这些挑战的重要手段。 一、大数据治理…...

云计算作业

关闭防火墙 停用Linux 挂载 下载nginx程序 启动nginx程序 连接网卡配置文件并且修改 更改模式为静态手动&#xff0c;并且分别修改ip地址&#xff0c;网关地址&#xff0c;dns 激活 创建自定义文件 定义server模块 监听地址 设置目录 匹配 激活网址根目录 创建目录文…...

复制文件到U盘提示:对于目标文件系统,文件过大

查看U盘属性的文件系统是否为FAT32&#xff0c;需将其改为NTFS 方法一 Win R 输入cmd打开命令行&#xff0c;输入以下命令&#xff08;注&#xff1a;f为U盘盘符&#xff09; convert f: /fs:ntfs /x方法二 格式化U盘&#xff0c;右键点击U盘进行格式化&#xff0c;文件系…...

SpringBoot+Swagger2.7.0实现汉化(2.8.0不行)

场景 SpringBootSwagger2实现可视化API文档流程&#xff1a; SpringBootSwagger2实现可视化API文档流程_swagger 可视化端口-CSDN博客 上面SpringBoot中使用swagger的效果 上面使用的是swagger2.8.0,且在线API是英文的。现在要将其进行汉化。 汉化效果 实现 首先打开sprin…...

c++ 散列表

散列表&#xff08;Hash Table&#xff09;是一种高效的数据结构&#xff0c;广泛用于实现快速的键值对存储。 基本概念 散列表使用哈希函数将键映射到数组的索引。其主要优点在于平均情况下提供常数时间复杂度的查找、插入和删除操作。 哈希函数: 将键映射到一个固定大小的…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...