文章内容
- 一、点击图标之后:Intent 里带了什么
- 1.1 Launcher 补上 NEWTASK
- 1.2 Activity 把请求交给 Instrumentation
- 二、systemserver 中的 Intent
- 2.1 resolveActivity 固定实际组件
- 2.2 ActivityRecord 与 ActivityStack 的创建边界
- 2.3 launchMode 与 flags 共同决定复用
- 三、onPause
- 3.1 第一次 resume 提前返回
- 3.2 RESUMED 变为 PAUSING
- 3.3 应用完成 onPause 后回报 AMS
- 四、“进程存在吗”:进程名与 uid 的来历
- 4.1 查询键是 processName 与 uid
- 4.2 processName 在安装解析期确定
- 4.3 uid 从 10000 起分配
- 4.4 ProcessMap 是两级映射
- 五、进程不存在时会发生什么
- 六、进程已就绪:Activity 实例怎样创建
- 6.1 AMS 发出 scheduleLaunchActivity
- 6.2 主线程把创建与 resume 分开处理
- 6.3 反射、attach 与 onCreate
- 6.4 attach 建立 Context 与 Window
- 七、onResume 与界面出现:DecorView 何时交给 WindowManager
- 7.1 Activity 为什么也是 Context
- 7.2 onResume 后才把 DecorView 加入窗口
- 7.3 Application 在消息顺序中的位置
- 八、小结
- 8.1 可执行的验证入口
本文依据 corpus 仓库中的四段录屏整理,内容覆盖桌面入口、前一个 Activity 的暂停、目标进程判定,以及新 Activity 的创建与显示。
上一篇《Android-Framework(十五)-进程的 fork 创建过程》已经分析 startSpecificActivityLocked() 到 Zygote fork、ActivityThread.main()、attachApplication()、bindApplication() 和 Application 创建。本文重点放在 startActivity、两趟 resume,以及进程就绪后的 Activity 创建。

图 1 Launcher 请求 AMS 启动、AMS 请求当前应用暂停、AMS 请求目标应用创建 Activity;activityPaused() 与 attachApplication() 是两条回报方向的调用。
一、点击图标之后:Intent 里带了什么
1.1 Launcher 补上 NEW_TASK
Launcher3 从图标保存的 ShortcutInfo.intent 或 AppInfo.intent 取出 Intent,经安全模式检查后调用自己的 startActivity()。这个私有方法会补上 FLAG_ACTIVITY_NEW_TASK;当前用户分支最终调用继承自 Activity 的 startActivity(intent, optsBundle)。
源码位置:packages/apps/Launcher3/src/com/android/launcher3/Launcher.java:2643—2663、:2866—2868、:2944—2957。
// 定义图标或应用信息的启动入口 @Thunk void startAppShortcutOrInfoActivity(View v) { // 读取图标绑定的数据 Object tag = v.getTag(); // 声明快捷方式引用(应用信息场景下为空) final ShortcutInfo shortcut; // 声明本次要启动的 Intent final Intent intent; // 图标来自快捷方式时取快捷方式里保存的 Intent if (tag instanceof ShortcutInfo) { // 保存快捷方式对象 shortcut = (ShortcutInfo) tag; // 从快捷方式取得 Intent intent = shortcut.intent; // 图标来自应用信息时取应用主入口的 Intent } else if (tag instanceof AppInfo) { // 应用信息场景没有快捷方式对象 shortcut = null; // 从应用信息取得 Intent intent = ((AppInfo) tag).intent; // 其余类型无法解析出启动目标 } else { // 直接抛出参数异常 throw new IllegalArgumentException("Input must be a Shortcut or AppInfo"); } // 经安全检查后发起启动 boolean success = startActivitySafely(v, intent, tag); // 省略随后的启动统计上报与按下态保持,二者与启动链无关 // 结束入口方法 } // 定义带安全模式检查的包装方法 @Thunk boolean startActivitySafely(View v, Intent intent, Object tag) { // 记录启动结果 boolean success = false; // 安全模式下拒绝启动非系统应用 if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { // 提示用户安全模式的限制 Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); // 直接返回失败 return false; } // 捕获目标 Activity 不存在的异常 try { // 调用 Launcher 内部启动方法 success = startActivity(v, intent, tag); } catch (ActivityNotFoundException e) { // 提示目标 Activity 不存在 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); } // 返回启动结果 return success; // 结束包装方法 } // 定义 Launcher 内部启动方法 private boolean startActivity(View v, Intent intent, Object tag) { // 为桌面启动补上新任务标记 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 进入可能抛出安全异常的启动段 try { // 声明动画选项与目标用户,二者由被省略的动画分支和用户信息计算得到 Bundle optsBundle = null; // 默认面向当前用户 UserHandleCompat user = null; // 当前用户或未指定用户时走 Activity 自带的启动接口 if (user == null || user.equals(UserHandleCompat.myUserHandle())) { // 调用继承自 Activity 的启动接口 startActivity(intent, optsBundle); // 面向次要用户时改走 LauncherApps } else { // 通过 LauncherApps 为指定用户启动 launcherApps.startActivityForProfile(intent.getComponent(), user, // 传入来源坐标与动画选项 intent.getSourceBounds(), optsBundle); } // 未抛异常即视为启动成功 return true; // 权限不足时捕获安全异常 } catch (SecurityException e) { // 提示启动失败 Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); // 记录详细的权限错误 Log.e(TAG, "Launcher does not have the permission to launch " + intent, e); } // 异常分支落到这里,返回启动失败 return false; // 结束方法 }上面几个代码块的省略部分:startActivity(View, Intent, Object) 里的动画选项计算(ActivityOptions → optsBundle)、次要用户分支前的用户信息解析,以及入口方法末尾的启动统计上报;optsBundle 与 user 的取值都来自这些被省略的语句。
Intent 的直接目标由 ComponentName 表示,即包名与 Activity 类名。new Intent(this, TargetActivity.class) 会构造这种显式目标;action/category 则交给 PMS 间接解析。
桌面保存的 Intent 最终也要落到具体组件,后面的 AMS 状态机并不靠图标本身识别 Activity。
1.2 Activity 把请求交给 Instrumentation
Activity.startActivityForResult() 在没有父 Activity 时,把自身、应用线程 Binder、Activity token 和 Intent 一并传给 Instrumentation。
源码位置:frameworks/base/core/java/android/app/Activity.java:3914—3921。
// 定义带返回值的 Activity 启动入口 public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { // 顶层 Activity 走 Instrumentation if (mParent == null) { // 接收可能被监控器拦截的结果 Instrumentation.ActivityResult ar = // 把启动所需对象交给 Instrumentation mInstrumentation.execStartActivity( // 传入上下文、应用线程、token、目标和选项 this, mMainThread.getApplicationThread(), mToken, this, // 传入 Intent、请求码与启动选项 intent, requestCode, options); // 结束顶层 Activity 分支 } // 结束启动入口 }Instrumentation 先处理 ActivityMonitor,再准备跨进程数据。下面保留真正进入 AMS 的部分。
源码位置:frameworks/base/core/java/android/app/Instrumentation.java:1504—1517。
// 捕获 Binder 调用异常 try { // 把旧式 stream extra 迁移为 ClipData intent.migrateExtraStreamToClipData(); // 准备让 Intent 离开当前进程 intent.prepareToLeaveProcess(); // 取得 AMS 代理并发起启动 int result = ActivityManagerNative.getDefault() // 通过 IActivityManager 传递完整启动参数 .startActivity(whoThread, who.getBasePackageName(), intent, // 解析 MIME 类型 intent.resolveTypeIfNeeded(who.getContentResolver()), // 传递 token、嵌入标识和请求码 token, target != null ? target.mEmbeddedID : null, // 传递启动标志、性能信息和选项 requestCode, 0, null, options); // 把服务端错误转换为调用方异常 checkStartActivityResult(result, intent); // 处理远程调用异常 } catch (RemoteException e) { // 包装为运行时异常 throw new RuntimeException("Failure from system", e); // 结束异常处理 }ActivityManagerNative.getDefault() 在应用侧返回 ActivityManagerProxy。代理把参数写进 Parcel,并以 START_ACTIVITY_TRANSACTION 发给 system_server。
源码位置:frameworks/base/core/java/android/app/ActivityManagerNative.java:2631—2662。
// 实现 IActivityManager 的启动接口 public int startActivity(IApplicationThread caller, String callingPackage, Intent intent, // 接收解析类型、返回目标与请求参数 String resolvedType, IBinder resultTo, String resultWho, int requestCode, // 接收启动标志、性能信息和选项 int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException { // 创建请求数据 Parcel Parcel data = Parcel.obtain(); // 创建响应 Parcel Parcel reply = Parcel.obtain(); // 写入接口令牌 data.writeInterfaceToken(IActivityManager.descriptor); // 写入调用方应用线程 Binder data.writeStrongBinder(caller != null ? caller.asBinder() : null); // 把 Intent 序列化到 Parcel intent.writeToParcel(data, 0); // 发起同步 Binder 事务 mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0); // 读取服务端异常 reply.readException(); // 读取启动结果码 int result = reply.readInt(); // 返回结果码 return result; // 结束代理方法 }二、system_server 中的 Intent
2.1 resolveActivity 固定实际组件
startActivityMayWait() 会复制客户端 Intent,避免修改调用方对象,再让 PMS 解析目标。resolveActivity() 使用 MATCH_DEFAULT_ONLY 取得 ActivityInfo,并把解析出的包名、类名写回副本的 component。
源码位置:frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java:863—906、:925—943、:1045—1049。
// 定义 Intent 解析方法 ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags, // 接收性能信息和用户标识 ProfilerInfo profilerInfo, int userId) { // 声明解析结果 ActivityInfo aInfo; // PMS 是跨进程接口,调用可能抛出 RemoteException try { // 调用 PMS 解析 Intent ResolveInfo rInfo = // 取得 PackageManager 的 Binder 接口 AppGlobals.getPackageManager().resolveIntent( // 传入 Intent 与 MIME 类型 intent, resolvedType, // 只匹配默认入口,并合并 AMS 使用的 PMS 标志 PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS, userId); // 从 ResolveInfo 取出 ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null; // 跨进程调用失败时视为解析失败 } catch (RemoteException e) { // 置空结果 aInfo = null; } // 解析成功后固定目标组件 if (aInfo != null) { // 用应用包名与 Activity 类名覆盖 component intent.setComponent(new ComponentName( // 传入包名和类名 aInfo.applicationInfo.packageName, aInfo.name)); } // 返回 ActivityInfo return aInfo; // 结束解析方法 } // 省略调用身份计算、IntentFirewall 校验与 heavy-weight 进程切换等前置分支 // 复制客户端传入的 Intent,避免修改调用方对象 intent = new Intent(intent); // 解析出 ActivityInfo ActivityInfo aInfo = // 调用前面的解析方法 resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId); // 省略容器、任务与调用身份的组装 // 把解析结果交给统一启动入口 int res = startActivityLocked(caller, intent, resolvedType, aInfo, // 传递语音会话与返回目标 voiceSession, voiceInteractor, resultTo, resultWho, // 传递调用身份与启动参数 requestCode, callingPid, callingUid, callingPackage, // 传递真实身份、安全标志和容器 realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity, // 传递 component 状态、容器和指定任务 componentSpecified, null, container, inTask);ActivityInfo 同时携带 theme、launchMode 和 applicationInfo;
后者又提供 uid 与默认进程信息。action/category 的间接匹配到这里已经结束,后续代码面对的是确定的 Activity 类型与组件。
2.2 ActivityRecord 与 ActivityStack 的创建边界
权限、IntentFirewall 和控制器检查通过后,startActivityLocked() 创建 ActivityRecord。任务栈选择发生在后续计算中;只有没有可用应用栈时,computeStackFocus() 才调用 createStackOnDisplay(),而该方法创建 ActivityContainer,其内部持有 ActivityStack。
源码位置:ActivityStackSupervisor.java:1636—1640、:1805—1809、:2939—2948。
// 为这次启动创建服务端 Activity 记录 ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, // 保存 Intent、解析类型、ActivityInfo 与配置 intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, // 保存请求码、组件状态、语音状态、监督器、容器和选项 requestCode, componentSpecified, voiceSession != null, this, container, options); // 需要回传记录时写入结果数组 if (outActivity != null) { // 返回新建的 ActivityRecord outActivity[0] = r; // 结束结果分支 } // 省略挂起事务处理与权限、控制器检查,它们位于本次创建之前 // computeStackFocus() 内部:确实找不到可用应用栈时才创建新栈 // 没有可用应用栈时创建一个栈 stack = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY); // 返回新栈 return stack; // createStackOnDisplay():创建 ActivityContainer,再返回容器持有的 ActivityStack ActivityStack createStackOnDisplay(int stackId, int displayId) { // 找到显示屏记录 ActivityDisplay activityDisplay = mActivityDisplays.get(displayId); // 找不到显示屏时拒绝创建 if (activityDisplay == null) { // 返回空结果 return null; // 结束显示屏检查 } // 创建持有 ActivityStack 的容器 ActivityContainer activityContainer = new ActivityContainer(stackId); // 登记容器 mActivityContainers.put(stackId, activityContainer); // 把容器附着到显示屏 activityContainer.attachToDisplayLocked(activityDisplay); // 返回容器中的 ActivityStack return activityContainer.mStack; // 结束创建方法 }因此,“启动 Activity 就新建一个 ActivityStack”并不准确。ActivityRecord 每次请求都会参与构造;ActivityStack 要等栈选择逻辑找不到合适对象时才创建。
2.3 launchMode 与 flags 共同决定复用
startActivityUncheckedLocked() 把 manifest 中的四种 launchMode 转成三个布尔量:standard 对应三者都为 false,另外三种分别命中。documentLaunchMode 还会改写 document 相关 flags。
源码位置:ActivityStackSupervisor.java:1828—1873、:2146—2172。
// 识别 singleTop final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP; // 识别 singleInstance final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE; // 识别 singleTask final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK; // 读取 Intent 携带的启动标志 int launchFlags = intent.getFlags(); // documentLaunchMode 对 flags 的改写位于 :1848—1866,本文不展开 // ……以下片段往前与上面相隔约 300 行:中间省略 reuseTask 声明(:1934)与 intentTask/intentActivity 的任务查找逻辑…… // 同时带 NEW_TASK 与 CLEAR_TASK 时直接清空旧任务(:2146 起,缩进保持原方法嵌套层级) if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) // 要求两个标志同时存在 == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) { // 复用目标 Activity 所在任务 reuseTask = intentActivity.task; // 清空任务内容 reuseTask.performClearTaskLocked(); // 更新任务的根 Intent reuseTask.setIntent(r); // CLEAR_TOP、singleInstance 或 singleTask 进入顶部清理逻辑 } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 // 合并两种 launchMode 条件 || launchSingleInstance || launchSingleTask) { // 清理目标之上的 Activity,并取得可复用记录 ActivityRecord top = // 按启动标志执行清理 intentActivity.task.performClearTaskLocked(r, launchFlags); // 找到可复用 Activity 时投递新 Intent if (top != null) { // 记录新 Intent 事件 ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, // 传入新记录与目标任务 r, top.task); // 把 Intent 交给已有实例,应用侧即 onNewIntent() top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage); // 找不到可复用记录时转入“重置任务”分支(:2173—2185,本文从略) } else { // ……省略 addingToTask、sourceRecord 与 targetStack 的重置处理…… } // 结束 CLEAR_TOP 与 singleTask 分支 }FLAG_ACTIVITY_NEW_TASK 会参与任务查找与创建,但它不等于无条件创建新 Task。命中已有任务及 SINGLE_TASK/CLEAR_TOP 等条件时,框架可清理顶部记录并调用 deliverNewIntentLocked();应用侧收到的是 onNewIntent(),而不是另一个 Activity 实例。

图 2 resolveActivity() 把 Intent 变成 ActivityInfo 与 ActivityRecord,startActivityUncheckedLocked() 再结合启动模式与 flags 决定复用任务还是新建栈。
三、onPause
3.1 第一次 resume 提前返回
resumeTopActivityInnerLocked() 先取得栈顶可运行记录。没有目标时才回到 Home;已有 mResumedActivity 时,系统发起 pause,并在 pausing == true 时立即返回。next.app == null 或 next.app.thread == null 时会落入 if/else 的 else 分支,只有后续一趟 resume 才能到达那里。
源码位置:frameworks/base/services/core/java/com/android/server/am/ActivityStack.java:1580—1608、:1723—1745、:1990—2008。
// 取得栈顶未结束的 Activity final ActivityRecord next = topRunningActivityLocked(null); // 没有目标 Activity 时处理返回 Home if (next == null) { // 计算应返回的任务类型 final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ? // 在 Home 与上一个任务类型之间选择 HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo(); // 只在 Home 显示屏恢复 Launcher return isOnHomeDisplay() && // 恢复 Home 任务 mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason); // 结束空目标分支 } // 先暂停其他栈 boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause); // 当前栈已有 resumed Activity 时也发起暂停 if (mResumedActivity != null) { // 合并当前栈的暂停结果 pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause); // 结束当前栈检查 } // 只要仍在等待 pause 就结束本趟 resume if (pausing) { // 返回并等待应用回报 return true; // 结束等待分支 } // 进程记录存在且应用线程已绑定:可以直接尝试恢复(:1860 起,内层判断本文从略) if (next.app != null && next.app.thread != null) { // ……省略配置检查、结果与新 Intent 投递,以及“进程曾经死亡需要重启”的子分支(:1900—1975)…… // 正常路径直接完成 resume,可见性置位并回调 onResume try { // 标记该记录可见 next.visible = true; // 完成 resume:回调 onResume 并处理加窗(见第七节) completeResumeLocked(next); // 恢复过程抛异常时放弃这个 Activity } catch (Exception e) { // 结束当前 Activity,让下一趟 resume 处理后续记录 requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, "resume-exception", true); // 返回本趟处理结束 return true; } // 进程不存在,或者记录存在但应用线程尚未绑定 } else { // 落入 else 分支才会要求启动目标 Activity,第三个参数表示需要重新启动 mStackSupervisor.startSpecificActivityLocked(next, true, true); // 结束进程判断 }3.2 RESUMED 变为 PAUSING
startPausingLocked() 把当前记录从 mResumedActivity 移到 mPausingActivity,状态改为 PAUSING,再通过 IApplicationThread.schedulePauseActivity() 反向通知应用进程。
源码位置:ActivityStack.java:816—890。
// 定义 pause 发起方法 final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming, // 接收是否等待回报的标志 boolean dontWait) { // 取得当前 resumed Activity ActivityRecord prev = mResumedActivity; // 当前栈没有已恢复的 Activity 时不需要真正发起 pause if (prev == null) { // 不是在 resuming 流程里调用,说明状态异常 if (!resuming) { // 触发一次栈顶恢复以自我修正 mStackSupervisor.resumeTopActivitiesLocked(); } // 直接返回:没有 pause 需要等待 return false; } // 清空 resumed 槽位 mResumedActivity = null; // 保存正在暂停的 Activity mPausingActivity = prev; // 保存最近暂停记录 mLastPausedActivity = prev; // 把状态切换为 PAUSING prev.state = ActivityState.PAUSING; // 确认应用线程可以调用 if (prev.app != null && prev.app.thread != null) { // 捕获反向 Binder 调用异常 try { // 记录 pause 事件 EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY, // 写入用户、对象标识和组件名 prev.userId, System.identityHashCode(prev), // 写入短组件名 prev.shortComponentName); // 通知应用主线程执行 pause prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing, // 传入离开、配置变化和等待标志 userLeaving, prev.configChangeFlags, dontWait); // 处理应用进程异常 } catch (Exception e) { // 应用进程可能已经死亡,清空暂停中的记录 mPausingActivity = null; // 同步清空最近暂停记录 mLastPausedActivity = null; } // 应用线程不可用时同样清空暂停状态 } else { // 没有可通知的应用线程,直接视为暂停完成 mPausingActivity = null; // 清空最近暂停记录 mLastPausedActivity = null; } // ……以下省略:key dispatch 暂停、dontWait 立即收尾与 PAUSE_TIMEOUT 消息投递…… // mPausingActivity != null 时方法返回 true,第一趟 resume 至此结束这是启动链的第二次跨进程,方向与第一次相反:system_server 调用当前 Activity 所在进程。
3.3 应用完成 onPause 后回报 AMS
应用主线程的 handlePauseActivity() 会在需要时调用 onUserLeaving(),再进入 performPauseActivity()。Instrumentation 调用 Activity 的 pause 入口;Activity.performPause() 内部先分发 Fragment pause,随后调用 onPause()。框架用 mCalled 检查子类是否调用 super.onPause()。
源码位置:frameworks/base/core/java/android/app/ActivityThread.java:3305—3328、:3343—3387,Activity.java:6344—6352。
// 处理主线程收到的 pause 消息 private void handlePauseActivity(IBinder token, boolean finished, // 接收离开、配置变化和回报控制参数 boolean userLeaving, int configChanges, boolean dontReport) { // 按 token 查找客户端记录 ActivityClientRecord r = mActivities.get(token); // 有效记录才执行暂停 if (r != null) { // 用户主动离开时分发对应回调 if (userLeaving) { // 调用 onUserLeaving 链路 performUserLeavingActivity(r); // 结束用户离开分支 } // 执行 Activity pause performPauseActivity(token, finished, r.isPreHoneycomb()); // 需要回报时通知 AMS if (!dontReport) { // 取得 AMS 代理并报告完成 ActivityManagerNative.getDefault().activityPaused(token); // 结束回报分支 } // 结束记录检查 } // 结束消息处理 } // 执行客户端记录的 pause final Bundle performPauseActivity(ActivityClientRecord r, boolean finished, // 接收是否保存状态 boolean saveState) { // 重置 super 调用标志 r.activity.mCalled = false; // 通过 Instrumentation 调用 Activity.onPause mInstrumentation.callActivityOnPause(r.activity); // 子类没有调用 super 时抛出异常 if (!r.activity.mCalled) { // 构造生命周期契约异常 throw new SuperNotCalledException( // 写入组件与错误信息 "Activity " + r.intent.getComponent().toShortString() + // 说明缺少 super.onPause " did not call through to super.onPause()"); // 结束 super 检查 } // 标记客户端记录已暂停 r.paused = true; // 结束 pause 执行方法 } // 定义 Activity 内部 pause 入口 final void performPause() { // 向 Fragment 分发 pause mFragments.dispatchPause(); // 重置 Activity 的 super 调用标志 mCalled = false; // 调用应用覆写的 onPause onPause(); // 清除 resumed 状态 mResumed = false; // 结束 Activity pause }performPause() 与 performPauseActivity() 还有两处省略:前者开头的 mDoReportFullyDrawn = false;以及 performPause() 末尾 targetSdkVersion >= GINGERBREAD 时的 mCalled 校验(与 onCreate、onResume 用的是同一套生命周期契约)。
AMS 收到 activityPaused() 后,activityPausedLocked() 移除超时消息,并让 completePauseLocked(true) 收尾。这个 true 会推动下一轮栈恢复。源码位置:ActivityStack.java:928—950。
// 处理应用进程的 pause 完成回报 final void activityPausedLocked(IBinder token, boolean timeout) { // 按 token 查找服务端 ActivityRecord final ActivityRecord r = isInStackLocked(token); // 找到记录后处理 if (r != null) { // 移除 pause 超时消息 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); // 只接受当前正在暂停的记录 if (mPausingActivity == r) { // 完成暂停并继续 resume 流程 completePauseLocked(true); // 结束当前记录匹配分支 } // 结束记录检查 } // 结束回报处理 }一次启动请求由此分成两趟 resumeTopActivity:第一趟发出 pause 并返回;应用回报后,第二趟才继续到 startSpecificActivityLocked()。

图 3 第一趟只负责发出 schedulePauseActivity() 并返回;应用回报 activityPaused() 之后,第二趟才继续到 startSpecificActivityLocked()。
四、“进程存在吗”:进程名与 uid 的来历
4.1 查询键是 processName 与 uid
startSpecificActivityLocked() 不按包名或 pid 判断目标进程,而是用 ActivityRecord.processName 和 ApplicationInfo.uid 查询 ProcessRecord。记录存在且 app.thread 已绑定时直接进入 realStartActivityLocked();否则请求 AMS 创建进程。
源码位置:ActivityStackSupervisor.java:1365—1397。
// 启动指定 Activity void startSpecificActivityLocked(ActivityRecord r, // 接收恢复与配置检查参数 boolean andResume, boolean checkConfig) { // 用进程名和 uid 查询 ProcessRecord ProcessRecord app = mService.getProcessRecordLocked(r.processName, // 传入应用 uid r.info.applicationInfo.uid, true); // 进程及应用线程 Binder 都可用时复用进程 if (app != null && app.thread != null) { // 捕获远程调用异常 try { // 在现有进程中启动 Activity realStartActivityLocked(r, app, andResume, checkConfig); // 成功后返回 return; // 远程对象失效时转入进程重启 } catch (RemoteException e) { // 结束异常处理 } // 结束进程复用分支 } // 没有可用应用线程时请求创建进程 mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, // 标记启动原因和目标组件 "activity", r.intent.getComponent(), false, false, true); // 结束方法 }4.2 processName 在安装解析期确定
PackageParser 读取 manifest 的 android:process。Application 没有配置该属性时,解析结果先保持为空,PMS 后续用包名补齐默认 processName;组件没有单独配置时,则继承 Application 的 processName。以冒号开头的名字由 buildCompoundName() 拼成 包名:子进程名,完整名称经过校验后保留。
源码位置:frameworks/base/core/java/android/content/pm/PackageParser.java:1972—2017、:2618—2631。
// 解析复合名称(进程名、taskAffinity 都走这里) private static String buildCompoundName(String pkg, // 接收声明值、类型与错误输出 CharSequence procSeq, String type, String[] outError) { // 转为字符串 String proc = procSeq.toString(); // 读取首字符 char c = proc.charAt(0); // 冒号开头表示应用私有进程 if (pkg != null && c == ':') { // 私有进程名至少要有一个字符,即 ":x" 才合法 if (proc.length() < 2) { // 写入长度不合法的错误 outError[0] = "Bad " + type + " name " + proc + " in package " + pkg + ": must be at least two characters"; // 校验失败返回 null return null; } // 取冒号之后的子名称 String subName = proc.substring(1); // 校验子名称是否为合法标识符 String nameError = validateName(subName, false, false); // 校验失败时报错返回 if (nameError != null) { // 写入具体校验错误 outError[0] = "Invalid " + type + " name " + proc + " in package " + pkg + ": " + nameError; // 校验失败返回 null return null; } // 校验通过后拼成“包名:子进程名” return (pkg + proc).intern(); } // 非冒号开头时校验是否算合法的完整名称 String nameError = validateName(proc, true, false); // "system" 是保留名,允许例外放行 if (nameError != null && !"system".equals(proc)) { // 写入校验错误 outError[0] = "Invalid " + type + " name " + proc + " in package " + pkg + ": " + nameError; // 校验失败返回 null return null; } // 校验通过后原样保留 return proc.intern(); // 结束复合名称解析 } // 根据默认值和 manifest 声明计算进程名 private static String buildProcessName(String pkg, String defProc, // 接收进程声明、解析标志和错误输出 CharSequence procSeq, int flags, String[] separateProcesses, // 接收错误信息 String[] outError) { // 没有 android:process 时返回默认进程名 if (procSeq == null || procSeq.length() <= 0) { // 默认值由调用方给出:Application 级传 null,组件级传 Application 的 processName return defProc; // 结束默认分支 } // 校验并构造自定义进程名 return buildCompoundName(pkg, procSeq, "process", outError); // 结束进程名计算 } // Application 级调用点:默认值传 null,因此未配置 android:process 时这里返回 null // 把 manifest 中的 process 属性写入 ApplicationInfo ai.processName = buildProcessName(ai.packageName, null, pname, // 传入解析标志、独立进程配置和错误输出 flags, mSeparateProcesses, outError);上面 buildProcessName() 的 defProc 是默认值来源,真正的兜底发生在 PMS 里:Application 级调用传入的默认值是包名,组件级调用传入的默认值是 Application 已经算好的 processName。
源码位置:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java:5944—5950、:6844—6847、:7343—7347。
// 进程名缺失时的兜底:默认值取包名 private static String fixProcessName(String defProcessName, // 接收待修正的进程名与 uid String processName, int uid) { // 没有显式配置进程名时使用默认值 if (processName == null) { // 返回调用方给出的默认名 return defProcessName; } // 已配置则原样保留 return processName; } // Application 级:默认值传的是包名,所以未配置 android:process 的应用,进程名等于包名 pkg.applicationInfo.processName = fixProcessName( // 默认值:包名 pkg.applicationInfo.packageName, // 待修正值:manifest 解析结果,未配置时为 null pkg.applicationInfo.processName, // 传入应用 uid pkg.applicationInfo.uid); // 组件级:默认值传的是 Application 的 processName,这就是“组件继承应用进程名”的发生点 for (i=0; i<N; i++) { PackageParser.Activity a = pkg.activities.get(i); // 组件未配置 android:process 时用 Application 的进程名补齐 a.info.processName = fixProcessName(pkg.applicationInfo.processName, // 待修正值是组件自己的声明,未配置时为 null a.info.processName, pkg.applicationInfo.uid); mActivities.addActivity(a, "activity");两处调用拼出默认值的传递链:未配置 android:process 的应用,processName 等于包名;未配置 android:process 的组件,processName 等于所属应用的 processName。ActivityRecord 的构造函数再把 aInfo.processName 记成自己的 processName,AMS 后续查询用的就是这个值。
同一个应用的组件配置不同 android:process 时会进入不同进程;不同应用也可能声明相同的完整进程字符串,因此进程名不能单独作为身份。
4.3 uid 从 10000 起分配
PMS 的 Settings 在安装期维护 appId 槽位。newUserIdLPw() 从 mFirstAvailableUid 查找空位,没有可复用槽位时在列表末尾追加,并加上 Process.FIRST_APPLICATION_UID(10000)。sharedUserId 场景会复用 SharedUserSetting 的 appId,不走每包独立分配。
源码位置:frameworks/base/services/core/java/com/android/server/pm/Settings.java:3722—3739。
// 为包设置分配新的应用 appId private int newUserIdLPw(Object obj) { // 取得当前槽位数量 final int N = mUserIds.size(); // 从首个可用位置开始查找空槽 for (int i = mFirstAvailableUid; i < N; i++) { // 空槽可以重新使用 if (mUserIds.get(i) == null) { // 保存设置对象 mUserIds.set(i, obj); // 加上应用 uid 起点后返回 return Process.FIRST_APPLICATION_UID + i; // 结束空槽分支 } // 结束槽位扫描 } // 超出应用 uid 可分配范围时返回失败 if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) { // 返回无可用 id return -1; // 结束范围检查 } // 在列表末尾登记新对象 mUserIds.add(obj); // 返回从 10000 起算的 appId return Process.FIRST_APPLICATION_UID + N; // 结束分配方法 } // 省略 addUserIdLPw():它把已有 appId 写回 mUserIds 的对应槽位分配结果是 appId 而不是最终 uid,它写入 ApplicationInfo.uid 的位置在安装扫描里。源码位置:PackageManagerService.java:6743—6744。
// 把前面分配(或从磁盘恢复)的 appId 写进 ApplicationInfo pkg.applicationInfo.uid = pkgSetting.appId; // 把 PackageSetting 挂到 Package 的 extras 上,便于后续反查 pkg.mExtras = pkgSetting;这里分配的是 appId 基础;多用户设备上的实际 uid 还包含 userId 维度,UserHandle.getUid(userId, appId) 才是进程看到的完整 uid。本文讨论 AMS 查询时,直接使用 ApplicationInfo.uid 中已经计算好的值。
4.4 ProcessMap 是两级映射
AMS 的 mProcessNames 使用 ProcessMap<ProcessRecord>。外层键是进程名,内层 SparseArray 的键是 uid。
源码位置:frameworks/base/core/java/com/android/internal/app/ProcessMap.java:22—40。
// 定义按进程名和 uid 索引的容器public class ProcessMap<E> { // 外层按名称、内层按 uid 保存值 final ArrayMap<String, SparseArray<E>> mMap // 初始化两级映射 = new ArrayMap<String, SparseArray<E>>(); // 按二元组查询 public E get(String name, int uid) { // 先按进程名取得 uid 表 SparseArray<E> uids = mMap.get(name); // 没有该名称时返回空 if (uids == null) return null; // 再按 uid 取值 return uids.get(uid); // 结束查询 } // 按二元组写入 public E put(String name, int uid, E value) { // 先查找名称对应的 uid 表 SparseArray<E> uids = mMap.get(name); // 首次出现该名称时创建内层表 if (uids == null) { // 创建小容量 SparseArray uids = new SparseArray<E>(2); // 登记到外层映射 mMap.put(name, uids); // 结束初始化分支 } // 按 uid 保存进程记录 uids.put(uid, value); // 返回写入值 return value; // 结束写入 }// 结束容器定义}getProcessRecordLocked() 对 SYSTEM_UID(1000)另设选择规则,普通应用则直接使用二元组查询。
源码位置:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java:2984—3021。
// 按进程名和 uid 查找 ProcessRecord final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) { // system uid 允许在同名的非应用进程中选择 if (uid == Process.SYSTEM_UID) { // 取得该进程名对应的全部 uid 记录 SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName); // 没有同名记录时返回空 if (procs == null) return null; // 保存同名进程数量 final int procCount = procs.size(); // 遍历同名进程 for (int i = 0; i < procCount; i++) { // 取得候选 uid final int procUid = procs.keyAt(i); // 排除应用 uid 和其他用户 if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) { // 继续检查下一个候选 continue; // 结束候选过滤 } // 返回系统进程记录 return procs.valueAt(i); // 结束遍历 } // 结束系统 uid 分支 } // 普通路径使用进程名与 uid 二元组 ProcessRecord proc = mProcessNames.get(processName, uid); // 返回查询结果 return proc; // 结束查询方法 }两个不同 uid 的应用即使都声明名为 push 的进程,也不会取得同一个 ProcessRecord。这个数据结构解释了 AMS 为什么同时需要 processName 与 uid。

图 4 左侧是进程名的来源链(manifest 声明优先,未声明时在 PMS 里回落到包名或 Application 的 processName),
右侧是 uid 的来源链(安装期从 10000 起分配 appId);两者一起构成 ProcessMap 的查询键。
五、进程不存在时会发生什么
startProcessLocked() 把默认入口设为 android.app.ActivityThread,随后调用 Process.start()。
源码位置:ActivityManagerService.java:3320—3332。
// 标记是否使用默认 Activity 进程入口 boolean isActivityProcess = (entryPoint == null); // 未指定入口时采用 ActivityThread if (entryPoint == null) entryPoint = "android.app.ActivityThread"; // 把进程参数交给 Process Process.ProcessStartResult startResult = Process.start(entryPoint, // 传入名称、uid、gid、补充组和调试参数 app.processName, uid, uid, gids, debugFlags, mountExternal, // 传入 SDK、安全、ABI 与指令集参数 app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, // 传入数据目录和入口参数 app.info.dataDir, entryPointArgs);后续链路是 Process.start() → Zygote socket → fork → ActivityThread.main() → attachApplication() → bindApplication() → Application 创建。新进程把 IApplicationThread 注册给 AMS 后,AMS 才能回到等待中的 ActivityRecord 并调用 realStartActivityLocked()。
六、进程已就绪:Activity 实例怎样创建
6.1 AMS 发出 scheduleLaunchActivity
realStartActivityLocked() 先把 ActivityRecord 关联到 ProcessRecord,并确认 app.thread 有效,然后通过应用线程 Binder 调用 scheduleLaunchActivity()。
源码位置:ActivityStackSupervisor.java:1179—1181、:1204—1215、:1224—1288。
// 在已绑定的应用进程中启动 Activity final boolean realStartActivityLocked(ActivityRecord r, // 接收进程记录和恢复参数 ProcessRecord app, boolean andResume, boolean checkConfig) // 声明远程调用异常 throws RemoteException { // 把 ActivityRecord 关联到目标进程 r.app = app; // 查找目标记录在进程列表中的位置 int idx = app.activities.indexOf(r); // 目标记录不在进程列表时加入 if (idx < 0) { // 保存 ActivityRecord app.activities.add(r); // 结束列表检查 } // 取得 Activity 所属任务 final TaskRecord task = r.task; // 取得任务所在栈 final ActivityStack stack = task.stack; // 确认应用线程已经绑定 if (app.thread == null) { // Binder 线程为空时按远程失败处理 throw new RemoteException(); // 结束线程检查 } // 反向通知应用进程启动 Activity app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken, // 传入对象标识、ActivityInfo 和全局配置 System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration), // 传入栈配置、兼容信息和来源包 new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage, // 传入语音、进程状态和保存状态 task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results, // 传入新 Intent、暂停标志、转场方向和性能信息 newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);这是本文第三次跨进程:system_server 把 ActivityInfo、token、Intent 和状态数据送到目标应用进程。
6.2 主线程把创建与 resume 分开处理
handleLaunchActivity() 初始化 WindowManager 全局状态,调用 performLaunchActivity() 创建对象并执行早期生命周期;成功后再调用 handleResumeActivity()。源码位置:frameworks/base/core/java/android/app/ActivityThread.java:2456—2482。
// 处理 LAUNCH_ACTIVITY 消息 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) { // 初始化窗口管理全局对象 WindowManagerGlobal.initialize(); // 创建并启动 Activity Activity a = performLaunchActivity(r, customIntent); // 创建成功后继续 resume if (a != null) { // 保存创建时配置 r.createdConfig = new Configuration(mConfiguration); // 保存旧状态 Bundle oldState = r.state; // 进入 onResume 与加窗阶段 handleResumeActivity(r.token, false, r.isForward, // 依据结束与暂停标记决定是否真正 resume !r.activity.mFinished && !r.startsNotResumed); // 结束创建成功分支 } // 结束消息处理 }performLaunchActivity() 负责实例化、attach 和 onCreate,handleResumeActivity() 负责 onResume 及窗口加入。
6.3 反射、attach 与 onCreate
performLaunchActivity() 从 LoadedApk 取得 ClassLoader,经 Instrumentation 反射创建 Activity,再创建 Activity 专用 Context,执行 attach(),最后调用 onCreate。源码位置:ActivityThread.java:2293—2376。
// 创建 Activity 并执行早期生命周期 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { // 取得目标 ActivityInfo ActivityInfo aInfo = r.activityInfo; // Intent 未显式指定组件时,用 PMS 解析结果回填 ComponentName component = r.intent.getComponent(); // 只在缺少组件时查询 PackageManager if (component == null) { // 用 Intent 自身解析出目标组件 component = r.intent.resolveActivity(mInitialApplication.getPackageManager()); // 把解析结果写回 Intent r.intent.setComponent(component); } // 配置了 activity-alias 时改用真实组件 if (r.activityInfo.targetActivity != null) { // 用包名和真实类名重建组件 component = new ComponentName(r.activityInfo.packageName, r.activityInfo.targetActivity); } // 初始化 Activity 引用 Activity activity = null; // 第一个 try:反射创建 Activity try { // 取得应用 ClassLoader java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); // 通过 Instrumentation 反射构造实例 activity = mInstrumentation.newActivity( // 传入 ClassLoader、类名与 Intent cl, component.getClassName(), r.intent); // 构造失败时交给 Instrumentation 决定是否吞掉异常 } catch (Exception e) { // 处理不了就包装成运行时异常抛出 if (!mInstrumentation.onException(activity, e)) { // 中断本次启动 throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } // 第二个 try:准备 Application,并完成 attach 与 onCreate try { // 取得或创建当前进程的 Application 实例 Application app = r.packageInfo.makeApplication(false, mInstrumentation); // Activity 构造成功后才建立运行环境 if (activity != null) { // 创建 Activity 级 Context Context appContext = createBaseContextForActivity(r, activity); // 从 ActivityInfo 加载标题 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); // 构造本次启动使用的配置快照 Configuration config = new Configuration(mCompatConfiguration); // 把 Context、线程、token、Application 等附着到实例 activity.attach(appContext, this, getInstrumentation(), r.token, // 传入标识、Application、Intent 与 ActivityInfo r.ident, app, r.intent, r.activityInfo, title, r.parent, // 传入嵌入标识、保留对象与配置 r.embeddedID, r.lastNonConfigurationInstances, config, // 传入 referrer 与语音交互器 r.referrer, r.voiceInteractor); // 重置 onCreate 的 super 调用标志 activity.mCalled = false; // 只保留非持久化分支:原方法此处是 if (r.isPersistable()) 的二选一 mInstrumentation.callActivityOnCreate(activity, r.state); // 校验子类是否调用 super.onCreate if (!activity.mCalled) { // 抛出生命周期契约异常 throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onCreate()"); } // 把实例保存到客户端记录 r.activity = activity; } // 启动失败时同样交给 Instrumentation } catch (Exception e) { // 处理不了就包装成运行时异常抛出 if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } // ……以下省略 onStart、onRestoreInstanceState、onPostCreate 与 mActivities.put()…… // 返回创建结果 return activity; // 结束创建方法 }performLaunchActivity() 的省略部分:第一个 try 之后还有 StrictMode 计数与 Intent 的额外数据类加载器设置;onCreate 之后还有 performStart()、onRestoreInstanceState()、onPostCreate(),以及把记录放进 mActivities。callActivityOnCreate 那一行在原方法里是 if (r.isPersistable()) 的二选一,本文只保留非持久化分支。
应用业务代码没有直接 new Activity()。框架掌握的是类名,运行时类型来自安装解析结果,因此必须用应用的 ClassLoader 反射实例化,再由框架注入运行所需对象。
Instrumentation 的对应构造入口最终调用 Class.newInstance(),随后执行 attach()。源
码位置:frameworks/base/core/java/android/app/Instrumentation.java:1037—1048。
// 以 Class 对象创建 Activity public Activity newActivity(Class<?> clazz, Context context, // 接收 token、Application、Intent 与 ActivityInfo IBinder token, Application application, Intent intent, ActivityInfo info, // 接收标题、父 Activity、嵌入标识和保留对象 CharSequence title, Activity parent, String id, // 声明反射构造异常 Object lastNonConfigurationInstance) throws InstantiationException, // 声明访问异常 IllegalAccessException { // 反射调用无参构造方法 Activity activity = (Activity)clazz.newInstance(); // 初始化线程引用 ActivityThread aThread = null; // 把框架运行环境附着到 Activity activity.attach(context, aThread, this, token, 0, application, intent, // 传入 Activity 元数据、标题、父对象和标识 info, title, parent, id, // 传入保留对象 (Activity.NonConfigurationInstances)lastNonConfigurationInstance, // 传入配置与其余交互参数 new Configuration(), null, null); // 返回实例 return activity; // 结束创建入口 }实际启动路径调用的是同类中的 ClassLoader 重载,它执行 cl.loadClass(className).newInstance();上面的重载完整展示了反射构造后紧接 attach() 的框架约定。
6.4 attach 建立 Context 与 Window
Activity.attach() 先让 ContextThemeWrapper 保存 base Context,然后创建 PhoneWindow,记录 UI 线程,并把 WindowManager 关联到 Activity token。源码位置:frameworks/base/core/java/android/app/Activity.java:6169—6222。
// 把框架运行环境附着到 Activity final void attach(Context context, ActivityThread aThread, // 接收 Instrumentation、token 与实例标识 Instrumentation instr, IBinder token, int ident, // 接收 Application、Intent 与 ActivityInfo Application application, Intent intent, ActivityInfo info, // 接收标题、父对象、嵌入标识和保留对象 CharSequence title, Activity parent, String id, // 接收配置、来源和语音交互器 NonConfigurationInstances lastNonConfigurationInstances, // 完成参数声明 Configuration config, String referrer, IVoiceInteractor voiceInteractor) { // 把 ContextImpl 保存为 mBase attachBaseContext(context); // 创建 Activity 的 PhoneWindow mWindow = new PhoneWindow(this); // 设置窗口回调为 Activity mWindow.setCallback(this); // 记录当前 UI 线程 mUiThread = Thread.currentThread(); // 保存 ActivityThread mMainThread = aThread; // 保存 Instrumentation mInstrumentation = instr; // 保存 Activity token mToken = token; // 为 PhoneWindow 配置 WindowManager mWindow.setWindowManager( // 从 base Context 取得窗口服务 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), // 传入 token、组件名和硬件加速标志 mToken, mComponent.flattenToString(), // 根据 ActivityInfo 决定硬件加速 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); // 保存 WindowManager mWindowManager = mWindow.getWindowManager(); // 结束附着方法 }
图 5 performLaunchActivity() 负责反射构造、attach 与 onCreate;handleResumeActivity() 负责 onResume,
并在窗口尚未加入时把 DecorView 交给 WindowManager,后续测量与绘制不再由 Activity 自己发起。
七、onResume 与界面出现:DecorView 何时交给 WindowManager
7.1 Activity 为什么也是 Context
createBaseContextForActivity() 调用 ContextImpl.createActivityContext(),再把 Activity 设为 outer Context。Application Context 和 Activity Context 都由 ContextImpl 承担实际能力,Activity 通过 mBase 转发 getResources() 等调用。源码位置:ActivityThread.java:2425—2435,frameworks/base/core/java/android/app/ContextImpl.java:1783—1794。
// 为 Activity 创建基础 Context private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { // 默认使用主显示屏 int displayId = Display.DEFAULT_DISPLAY; // 创建 Activity 专用 ContextImpl ContextImpl appContext = ContextImpl.createActivityContext( // 传入主线程、LoadedApk、显示屏和覆盖配置 this, r.packageInfo, displayId, r.overrideConfig); // 把 Activity 设为外层 Context appContext.setOuterContext(activity); // 以 ContextImpl 作为基础 Context Context baseContext = appContext; // 返回基础 Context return baseContext; // 结束 Context 创建 } // 创建 Application 级 ContextImpl static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { // LoadedApk 不能为空 if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); // 构造 Application Context return new ContextImpl(null, mainThread, // 使用无 Activity token、默认显示屏的参数 packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY); // 结束 Application Context 创建 } // 创建 Activity 级 ContextImpl static ContextImpl createActivityContext(ActivityThread mainThread, // 接收 LoadedApk、显示屏和覆盖配置 LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) { // LoadedApk 不能为空 if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); // 构造带显示屏配置的 Activity Context return new ContextImpl(null, mainThread, packageInfo, null, null, false, // 传入覆盖配置和显示屏标识 null, overrideConfiguration, displayId); // 结束 Activity Context 创建 }这条代理链是 Activity → mBase(ContextImpl)→ Resources/LoadedApk。构造 Activity 时 mBase 尚未建立;attach() 完成后,Context API 才具备 Activity 对应的资源和显示配置。
这条链路在应用进程侧的落点如下:performResumeActivity() 找到客户端记录后调用 Activity.performResume(),后者经 Instrumentation.callActivityOnResume() 触发 onResume(),并用同一个 mCalled 契约校验 super 调用。源码位置:ActivityThread.java:3071—3111、Activity.java:6303—6318、Instrumentation.java:1256—1269。
// ActivityThread 侧:找到客户端记录后交给 Activity.performResume public final ActivityClientRecord performResumeActivity(IBinder token, // 接收是否清除隐藏标记 boolean clearHide) { // 按 token 找到客户端记录 ActivityClientRecord r = mActivities.get(token); // 记录存在且 Activity 未结束才继续 if (r != null && !r.activity.mFinished) { // 需要时清除隐藏标记与“已启动其他 Activity”标记 if (clearHide) { // 允许窗口重新显示 r.hideForNow = false; // 复位已启动其他 Activity 的标记 r.activity.mStartedActivity = false; } // 真正触发 Activity 的 resume 流程 r.activity.performResume(); // 复位客户端记录的 paused / stopped 标记 r.paused = false; r.stopped = false; // 结束记录检查 } // 省略异常处理与挂起结果投递 // 返回客户端记录,供调用方决定要不要加窗 return r; // 结束 resume 执行 } // Activity 侧:performResume 先重启、再回调 onResume final void performResume() { // 先走 performRestart,必要时回调 onRestart performRestart(); // 执行 Fragment 挂起的操作 mFragments.execPendingActions(); // 重置 super 调用标志 mCalled = false; // 通过 Instrumentation 调用真正的 onResume mInstrumentation.callActivityOnResume(this); // 子类没有调用 super.onResume 时报错 if (!mCalled) { // 抛出生命周期契约异常 throw new SuperNotCalledException( "Activity " + mComponent.toShortString() + " did not call through to super.onResume()"); } // 结束 resume } // Instrumentation 侧:设置 mResumed 并调用 onResume public void callActivityOnResume(Activity activity) { // 标记 Activity 处于 resumed 状态 activity.mResumed = true; // 调用应用覆写的 onResume activity.onResume(); // 省略随之而来的 ActivityMonitor 遍历 // 结束 onResume 调用 }7.2 onResume 后才把 DecorView 加入窗口
handleResumeActivity() 先调用 performResumeActivity()。窗口尚未加入、Activity 未结束且应该可见时,代码取得 DecorView 和 WindowManager.LayoutParams,调用 wm.addView(decor, l)。源码位置:ActivityThread.java:3126—3170。
// 处理 Activity resume final void handleResumeActivity(IBinder token, // 接收可见性、转场方向和恢复标志 boolean clearHide, boolean isForward, boolean reallyResume) { // 执行 onResume 链路 ActivityClientRecord r = performResumeActivity(token, clearHide); // 记录有效时处理窗口 if (r != null) { // 取得 Activity final Activity a = r.activity; // 计算软键盘转场标志位 final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; // 默认认为本 Activity 会可见,前提是它没有在 resume 期间启动别的 Activity boolean willBeVisible = !a.mStartedActivity; // 确实启动过别的 Activity 时,需要向 AMS 再确认一次可见性 if (!willBeVisible) { // 跨进程查询自己是否仍然可见 try { // 用当前 token 反查可见性 willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible( a.getActivityToken()); // 查询失败时保留默认值 } catch (RemoteException e) { } } // 窗口尚未加入、Activity 未结束且应当可见时,建立视图根入口 if (r.window == null && !a.mFinished && willBeVisible) { // 取得 PhoneWindow r.window = r.activity.getWindow(); // 取得 DecorView View decor = r.window.getDecorView(); // 加窗前先设为不可见 decor.setVisibility(View.INVISIBLE); // 取得 Activity 的 WindowManager ViewManager wm = a.getWindowManager(); // 取得窗口布局参数 WindowManager.LayoutParams l = r.window.getAttributes(); // 保存 DecorView a.mDecor = decor; // 合并软键盘转场标志 l.softInputMode |= forwardBit; // 客户端允许显示时加入窗口 if (a.mVisibleFromClient) { // 标记窗口已经添加 a.mWindowAdded = true; // 把 DecorView 交给 WindowManagerImpl,后续由 ViewRootImpl 负责测量绘制 wm.addView(decor, l); // 结束可见性分支 } // 窗口已加入但当前不应显示时,先标记为隐藏 } else if (!willBeVisible) { // 暂时隐藏窗口 r.hideForNow = true; // 结束窗口处理分支 } // 结束记录检查 } // ……以下省略最终可见性判定与配置变化通知(:3181—3210)…… // 结束 resume 处理 } // Activity.setContentView 只是把请求转发给 Window public void setContentView(@LayoutRes int layoutResID) { // 让 PhoneWindow 安装应用布局 getWindow().setContentView(layoutResID); // 初始化 Decor 中的 ActionBar initWindowDecorActionBar(); // 结束 Activity 内容设置 } // PhoneWindow 安装布局资源 public void setContentView(int layoutResID) { // 第一条 if:决定内容容器是首次创建还是清空重用 // 内容容器尚未创建时安装 Decor,内部会生成 mContentParent if (mContentParent == null) { // 创建系统窗口布局并解析出内容容器 installDecor(); // 容器已存在且没有开启内容转场特性时,先清空旧视图 } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { // 移除原有的应用视图 mContentParent.removeAllViews(); // 结束容器处理 } // 第二条 if:与上面是否首次创建无关,只看是否开启内容转场特性 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { // 开启转场特性时用 Scene 承载新布局(本文材料未覆盖这条分支) final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); // 执行场景切换 transitionTo(newScene); // 未开启转场特性时直接把布局 inflate 进内容容器 } else { // 把应用布局加入 mContentParent,即系统布局中的 ID_ANDROID_CONTENT(android.R.id.content) mLayoutInflater.inflate(layoutResID, mContentParent); // 结束布局安装分支 } // 通知内容容器重新应用 WindowInsets mContentParent.requestApplyInsets(); // 结束 PhoneWindow 内容设置 } // 安装 Decor 与系统内容容器 private void installDecor() { // Decor 尚未创建时生成根视图 if (mDecor == null) { // 创建 DecorView mDecor = generateDecor(); // 让 DescendantFocusability 语义与本方法后续逻辑一致 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); // 结束 Decor 创建 } // 内容容器尚未创建时解析系统布局 if (mContentParent == null) { // generateLayout 解析系统布局,并从中取出 android.R.id.content mContentParent = generateLayout(mDecor); // 结束内容容器创建 } // 省略 Decor 其余初始化:ActionBar、标题栏、转场控件等 // 结束 Decor 安装 }同一个代码块还对应两组源码:Activity.java:2165—2168,以及 PhoneWindow.java:378—395、:3913、:3971—3981。为了只保留当前结论需要的分支,内容转场的 Scene 路径和 Decor 其余初始化已省略。
setContentView() 在未开启内容转场特性时只是把应用布局 inflate 到系统布局的 R.id.content 容器。此时完成的是 View 层级组装,尚未进行测量、布局和绘制。wm.addView() 之后由 WindowManagerImpl/WindowManagerGlobal 建立 ViewRootImpl,后者发起 measure/layout/draw,并通过 WMS 参与窗口管理。

图 6 应用布局只是挂在系统布局的 android.R.id.content 上;测量、布局与绘制由 wm.addView() 之后建立的
ViewRootImpl 发起,窗口的添加与合成归 WMS。
7.3 Application 在消息顺序中的位置
新进程路径中,handleBindApplication() 先创建 Application,安装 Provider 后调用 Application.onCreate();Activity 的启动消息随后才由主线程处理。
源码位置:ActivityThread.java:4680—4681、:4707。
// 创建或取得当前进程的 Application Application app = data.info.makeApplication(data.restrictedBackupMode, null); // 保存初始 Application mInitialApplication = app; // 调用 Application.onCreate mInstrumentation.callApplicationOnCreate(app);进程第一次创建时,Application 初始化先于 Activity 的 performLaunchActivity();进程已经存在时,AMS 可以直接发送 scheduleLaunchActivity()。
八、小结
从桌面点击到目标窗口加入 WindowManager,Android 6.0 的主干调用如下:
- Launcher 从图标取出显式 Intent,补
FLAG_ACTIVITY_NEW_TASK,经 Activity 与 Instrumentation 调用 AMS Binder 代理。 - system_server 中的
startActivityMayWait()让 PMS 把 Intent 解析为 ActivityInfo,startActivityLocked()创建 ActivityRecord,startActivityUncheckedLocked()结合 launchMode 和 flags 决定复用或创建任务状态。 - 当前 Activity 仍是 RESUMED 时,第一趟
resumeTopActivityInnerLocked()发出schedulePauseActivity()后返回。应用执行onPause()并回报activityPaused(),AMS 才发起第二趟 resume。 startSpecificActivityLocked()以(processName, uid)查询 ProcessRecord。目标进程不存在时进入 Zygote fork 链;进程和app.thread已就绪时进入realStartActivityLocked()。- AMS 反向调用
scheduleLaunchActivity()。应用主线程依次执行反射构造、attach()、onCreate()和onResume(),随后把 DecorView 交给 WindowManager。
8.1 可执行的验证入口
以下命令分别观察 Activity/Task、ProcessRecord 与事件顺序。Android 版本不同,事件字段可能变化。
# 查看 ActivityRecord、TaskRecord 与栈顶状态adb shell dumpsys activity activities# 查看进程名、uid、pid 和 ProcessRecordadb shell dumpsys activity processes# 观察进程启动、pause、新 Intent 与启动耗时事件adb logcat -b events | grep -E 'am_proc_start|am_pause_activity|am_new_intent|am_activity_launch_time'# 显式启动目标 Activity,便于重复冷启动与热启动实验adb shell am start -n <包名>/<Activity全类名>源码调试可以在 ActivityStackSupervisor.startSpecificActivityLocked() 与 ActivityThread.performLaunchActivity() 设置断点。第一次结束目标进程后启动,验证 startProcessLocked() 分支;第二次保留进程再次启动,验证直接进入 realStartActivityLocked()。若要观察两趟 resume,可再在 ActivityStack.startPausingLocked() 和 activityPausedLocked() 设置断点。
本文由 AgentWorkFlow 生成