Android-Framework(十六)-startActivity 的启动过程

文章内容

本文依据 corpus 仓库中的四段录屏整理,内容覆盖桌面入口、前一个 Activity 的暂停、目标进程判定,以及新 Activity 的创建与显示。

上一篇《Android-Framework(十五)-进程的 fork 创建过程》已经分析 startSpecificActivityLocked() 到 Zygote fork、ActivityThread.main()、attachApplication()、bindApplication() 和 Application 创建。本文重点放在 startActivity、两趟 resume,以及进程就绪后的 Activity 创建。

一次 startActivity 涉及的三段跨进程控制流

图 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。

java
    // 定义图标或应用信息的启动入口    @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。

java
    // 定义带返回值的 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。

java
        // 捕获 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。

java
    // 实现 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。

java
    // 定义 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;    // 结束解析方法    }
java
        // 省略调用身份计算、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。

java
        // 为这次启动创建服务端 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;        // 结束结果分支        }        // 省略挂起事务处理与权限、控制器检查,它们位于本次创建之前
java
            // 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。

java
        // 识别 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 实例。

Intent 从解析到任务决策的判定过程

图 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。

java
        // 取得栈顶未结束的 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。

java
    // 定义 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。

java
    // 处理主线程收到的 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。

java
    // 处理应用进程的 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()。

一次启动请求在 AMS 里被拆成两趟 resume

图 3 第一趟只负责发出 schedulePauseActivity() 并返回;应用回报 activityPaused() 之后,第二趟才继续到 startSpecificActivityLocked()。

四、“进程存在吗”:进程名与 uid 的来历

4.1 查询键是 processName 与 uid

startSpecificActivityLocked() 不按包名或 pid 判断目标进程,而是用 ActivityRecord.processName 和 ApplicationInfo.uid 查询 ProcessRecord。记录存在且 app.thread 已绑定时直接进入 realStartActivityLocked();否则请求 AMS 创建进程。

源码位置:ActivityStackSupervisor.java:1365—1397。

java
    // 启动指定 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。

java
    // 解析复合名称(进程名、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。

java
    // 进程名缺失时的兜底:默认值取包名    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。

java
    // 为包设置分配新的应用 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。

java
        // 把前面分配(或从磁盘恢复)的 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。

java
// 定义按进程名和 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。

java
    // 按进程名和 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。

进程身份由 processName 与 uid 两个维度共同确定

图 4 左侧是进程名的来源链(manifest 声明优先,未声明时在 PMS 里回落到包名或 Application 的 processName),
右侧是 uid 的来源链(安装期从 10000 起分配 appId);两者一起构成 ProcessMap 的查询键。

五、进程不存在时会发生什么

startProcessLocked() 把默认入口设为 android.app.ActivityThread,随后调用 Process.start()。
源码位置:ActivityManagerService.java:3320—3332。

java
            // 标记是否使用默认 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。

java
    // 在已绑定的应用进程中启动 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。

java
    // 处理 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。

java
    // 创建 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。

java
    // 以 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。

java
    // 把框架运行环境附着到 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();    // 结束附着方法    }

进程就绪后新 Activity 的创建两步

图 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。

java
    // 为 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。

java
    // 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。

java
    // 处理 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 处理    }
java
    // Activity.setContentView 只是把请求转发给 Window    public void setContentView(@LayoutRes int layoutResID) {        // 让 PhoneWindow 安装应用布局        getWindow().setContentView(layoutResID);        // 初始化 Decor 中的 ActionBar        initWindowDecorActionBar();    // 结束 Activity 内容设置    }
java
    // 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 内容设置    }
java
    // 安装 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。

java
            // 创建或取得当前进程的 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 的主干调用如下:

  1. Launcher 从图标取出显式 Intent,补 FLAG_ACTIVITY_NEW_TASK,经 Activity 与 Instrumentation 调用 AMS Binder 代理。
  2. system_server 中的 startActivityMayWait() 让 PMS 把 Intent 解析为 ActivityInfo,startActivityLocked() 创建 ActivityRecord,startActivityUncheckedLocked() 结合 launchMode 和 flags 决定复用或创建任务状态。
  3. 当前 Activity 仍是 RESUMED 时,第一趟 resumeTopActivityInnerLocked() 发出 schedulePauseActivity() 后返回。应用执行 onPause() 并回报 activityPaused(),AMS 才发起第二趟 resume。
  4. startSpecificActivityLocked() 以 (processName, uid) 查询 ProcessRecord。目标进程不存在时进入 Zygote fork 链;进程和 app.thread 已就绪时进入 realStartActivityLocked()。
  5. AMS 反向调用 scheduleLaunchActivity()。应用主线程依次执行反射构造、attach()、onCreate() 和 onResume(),随后把 DecorView 交给 WindowManager。

8.1 可执行的验证入口

以下命令分别观察 Activity/Task、ProcessRecord 与事件顺序。Android 版本不同,事件字段可能变化。

bash
# 查看 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 生成

觉得不错?给作者点个赞吧

分类:Android系统

评论 (0)

无需注册,留言经站长审核后公开。

加载评论中…

Android-Framework(十六)-startActivity 的启动过程 · Yinchen 的博客