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等也是按照类似方式来处理超时的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/56427.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ZBrush入门使用介绍——17、FiberMesh

大家好&#xff0c;我是阿赵。   继续介绍ZBrush的使用。这次来看看FiberMesh功能。这是一个可以模仿毛发的功能。 一、 使用FiberMesh的预览功能 先准备一个模型&#xff0c;并生成多边形网格 然后按着Ctrl&#xff0c;在模型的表面画一个遮罩。 找到FiberMesh功能&#…

JSP 的 response 和 session 内置对象

文章目录 前言一、response 内置对象 1.重定向网页2.处理 HTTP 文件头3.设置输出缓存二、session 内置对象 1.创建及获取客户会话2.会话中移动指定的绑定对象3.销毁 session 内置对象4.会话超时的管理5. session 对象的应用总结 前言 JSP 的 response 和 session 内置对像&…

HCIP——以太网交换安全(四)DHCP Snooping

目录 一、DHCP Snooping的知识点 二、DHCP Snooping实验拓扑 三、总结 一、DHCP Snooping的知识点 1.1、DHCP snooping 概述&#xff1a; ①DHCP Snooping使能DHCP的一种安全特性&#xff0c;用于保证DHCP客户端从合法的DHCP服务端获取IP地址。DHCP服务器记录DHCP客户端IP…

uniapp 省、市、区、乡镇 数据层级选择插件 Ba-DataPicker

Ba-DataPicker 是一款uniapp数据层级选择弹窗插件。支持省市区乡四级&#xff1b;支持自定义数据。 支持省、市、区、乡镇四级支持自定义数据支持字母检索 截图展示 支持定制、本地包、源码等&#xff0c;有建议和需要&#xff0c;请点击文章结尾“Uniapp插件开发”联系我&am…

Flink有状态计算

前言 状态是什么&#xff1f;状态就是数据&#xff0c;准确点说&#xff0c;状态是指 Flink 作业计算时依赖的历史数据或中间数据。如果一个 Flink 作业计算依赖状态&#xff0c;那它就是有状态计算的作业&#xff0c;反之就是无状态计算的作业。 举个例子&#xff0c;服务端…

AnaTraf | 提升网络稳定性与效率:深入解析网络流量采集分析与故障定位

目录 网络流量采集分析的核心价值 什么是网络流量采集分析&#xff1f; 网络流量分析的应用场景 利用流量分析优化企业网络 网络故障定位的关键步骤 故障定位的基本流程 常用故障定位方法 实用技巧 网络流量采集分析与故障定位的协同作用 整合流量分析提升故障响应速…

人脸识别-特征算法

文章目录 一、LBPH算法1.基本原理2.实现步骤3.代码实现 二、Eigenfaces算法1.特点2.代码实习 三、FisherFaces算法1.算法原理2.算法特点3.代码实现 四、总结 人脸识别特征识别器是数字信息发展中的一种生物特征识别技术&#xff0c;其核心在于通过特定的算法和技术手段&#xf…

开源 | Science子刊 | GCS轨迹优化方法

论文: https://arxiv.org/pdf/2205.04422 Github: https://github.com/RobotLocomotion/gcs-science-robotics 本文介绍了一种基于凸优化的高效运动规划方法&#xff0c;该方法能够在高维空间中可靠地规划出障碍物周围的轨迹。研究者们通过结合贝塞尔曲线和凸集图&#xff08…

大厂面试一上来就手撕 Transformer,心凉半截

在这两年&#xff0c;尤其是大模型问世之后&#xff0c;有关 Transformer 的面试题不仅数量众多&#xff0c;而且颇具新意。 今日&#xff0c;我将分享 18 道 Transformer 高频面试题&#xff08;如需获取更多专业面试题&#xff0c;扫描文末二维码即可&#xff09;&#xff0…

【超详细】TCP协议

TCP(Transmission Control Protocol 传输控制协议) 传输层协议有连接可靠传输面向字节流 为什么TCP是传输控制协议呢&#xff1f; 我们以前所看到的write接口&#xff0c;都是把用户级缓冲区的数据拷贝到发送缓冲区中&#xff0c;然后数据就由TCP自主决定了&#xff0c;所以…

Postman 如何测试入参是文件类型(File)参数接口

Postman 如何测试 File 类型参数 前提背景测试步骤1、打开 Postman 选择 POST 方法输入调用地址2、参数选择 Body 下的 form-data3、KEY 选择 File 选项&#xff0c;并填写 file 前提背景 springboot 项目&#xff0c;接口的参数是 File 类型&#xff0c;需要通过 Postman 测试…

js中map,filter,find,foreach的用法介绍

js中map&#xff0c;filter&#xff0c;find&#xff0c;foreach的用法介绍 在 JavaScript 中&#xff0c;数组提供了一些常用的迭代方法&#xff0c;如 map、filter、find 和 forEach&#xff0c;这些方法允许你对数组中的每个元素进行操作&#xff0c;下面是它们的用法和区别…

2024台州赛CTFwp

备注&#xff1a; 解题过程中&#xff0c;关键步骤不可省略&#xff0c;不可含糊其辞、一笔带过。解题过程中如是自己编写的脚本&#xff0c;不可省略&#xff0c;不可截图&#xff08;代码字体可以调小&#xff1b;而如果代码太长&#xff0c;则贴关键代码函数&#xff09;。…

etcd集群修复异常节点

描述&#xff1a;仅一个 etcd 节点状态异常且无法自愈。 方案&#xff1a;删除异常节点&#xff0c;然后重新加入。 官方文档&#xff1a; &#xfeff;How to Add and Remove Members | etcd&#xfeff; &#xfeff;Runtime reconfiguration | etcd

微信小程序添加删除线和下划线

微信小程序如何添加删除线和下划线&#xff1f; 小程序官方提供的html标签&#xff0c;跟传统的HTML有所差异&#xff0c;下面关于下划线和删除线的操作。 在对应的标签中添加css样式即可&#xff1a; text-decoration:underline; //下划线显示效果如下&#xff1a; text-de…

Gitxray:一款基于GitHub REST API的网络安全工具

关于Gitxray Gitxray是一款基于GitHub REST API的网络安全工具&#xff0c;支持利用公共 GitHub REST API 进行OSINT、信息安全取证和安全检测等任务。 Gitxray&#xff08;Git X-Ray 的缩写&#xff09;是一款多功能安全工具&#xff0c;专为 GitHub 存储库而设计。它可以用于…

【大数据技术基础 | 实验三】HDFS实验:部署HDFS

文章目录 一、实验目的二、实验要求三、实验原理&#xff08;一&#xff09;分布式文件系统&#xff08;二&#xff09;HDFS&#xff08;三&#xff09;HDFS基本命令&#xff08;四&#xff09;HDFS适用场景 四、实验环境五、实验内容和步骤&#xff08;一&#xff09;在master…

优阅达携手 Theobald 亮相新加坡科技周,助力企业 SAP 数据集成与应用

针对不同用户需求量身定制解决方案&#xff0c;帮助企业轻松应对从数据提取到分析、从开发到流程管理的 SAP 数据挑战。 上周&#xff0c;2024 新加坡科技周在滨海湾金沙会议展览中心圆满落幕。在为期两天的活动中&#xff0c;七大专题展览同时进行&#xff0c;超过 2,000 家…

【解决】webstrom uniapp rpx格式化空格 报错飘红

解决办法 1、安装 wechat mini program support 插件 2. 设置 wechat mini program 里小程序支持选为启用 3. 重新格式化显示正常&#xff0c;也不飘红了 注意要style开启scss支持lang"scss"&#xff0c;否则也会飘红报错 <style lang"scss"><…

『Mysql集群』Mysql高可用集群之读写分离(二)

前言 主从复制: 解决了Mysql的单点故障问题以及提高MySQL的整体服务性能. 读写分离: 解决的是数据库的读性能问题,分担主库的压力&#xff0c;提高系统的可用性和稳定性。 分库分表: 数据库分表可以解决单表海量数据的查询性能问题&#xff0c;分库可以解决单台数据库的并发…