跳转至

应用启动流程

1. 桌面应用的启动

我们每天看到的桌面实际上也是一个 App,aosp 中默认为 Launcher3,位于 /packages/apps/Launcher3/

SystemServer.runstartOtherServices 中会进入 ActivityManagerService.systemReady 从而调用 mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");

/frameworks/base/services/java/com/android/server/SystemServer.java
// line 796-797
    private void run() {
        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
//...
// line 984-994 调用 startOtherServices 方法
        try {
            t.traceBegin("StartServices");
            startBootstrapServices(t);
            startCoreServices(t);
            startOtherServices(t);
            startApexServices(t);
            // Only update the timeout after starting all the services so that we use
            // the default timeout to start system server.
            updateWatchdogTimeout(t);
            CriticalEventLog.getInstance().logSystemServerStarted();
        } catch (Throwable ex) {
/frameworks/base/services/java/com/android/server/SystemServer.java
// line 1513-1518
    /**
     * Starts a miscellaneous grab bag of stuff that has yet to be refactored and organized.
     */
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        t.traceBegin("startOtherServices");
        mSystemServiceManager.updateOtherServicesStartIndex();
//...
// line 3217-3223 调用 mActivityManagerService.systemReady
        // We now tell the activity manager it is okay to run third party
        // code.  It will call back into us once it has gotten to the state
        // where third party code can really run (but before it has actually
        // started launching the initial applications), for us to complete our
        // initialization.
        mActivityManagerService.systemReady(() -> {
            Slog.i(TAG, "Making services ready");
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
// line 8970-8975
    /**
     * Ready. Set. Go!
     */
    public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
        t.traceBegin("PhaseActivityManagerReady");
        mSystemServiceManager.preSystemReady();
//...
// line 9129-9139 调用 mAtmInternal.startHomeOnAllDisplays
            boolean isBootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;

            // Some systems - like automotive - will explicitly unlock system user then switch
            // to a secondary user.
            // TODO(b/266158156): this workaround shouldn't be necessary once we move
            // the headless-user start logic to UserManager-land.
            if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
                t.traceBegin("startHomeOnAllDisplays");
                mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
                t.traceEnd();
            }

这里调用的是 ActivityTaskManagerInternal。它是系统进程内部使用的抽象接口,真正的实现是 ActivityTaskManagerService 内部的 LocalService。AMS 通过 LocalServices.getService(ActivityTaskManagerInternal.class) 拿到这个内部服务,因此这里最终会进入 ActivityTaskManagerService.LocalService.startHomeOnAllDisplays()

/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
1
2
3
4
5
6
7
8
9
// line 6038
    final class LocalService extends ActivityTaskManagerInternal {
// line 6636-6641
        @Override
        public boolean startHomeOnAllDisplays(int userId, String reason) {
            synchronized (mGlobalLock) {
                return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
            }
        }

在这里进入了 RootWindowContainerstartHomeOnAllDisplays

我们先只看主屏幕的链路:

/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
// line 1296-1303
    boolean startHomeOnAllDisplays(int userId, String reason) {
        boolean homeStarted = false;
        for (int i = getChildCount() - 1; i >= 0; i--) {
            final int displayId = getChildAt(i).mDisplayId;
            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
        }
        return homeStarted;
    }
// line 1315-1321
    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
        return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */,
                false /* fromHomeKey */);
    }

    boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
            boolean fromHomeKey) {
// line 1328-1333
        final DisplayContent display = getDisplayContentOrCreate(displayId);
        return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
                        result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                                allowInstrumenting, fromHomeKey),
                false /* initValue */);
    }
// line 1346-1347
    boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
            boolean allowInstrumenting, boolean fromHomeKey) {
// line 1402-1403
        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                taskDisplayArea);

ActivityStartController.startHomeActivity() 的参数 homeIntent 是由 ActivityTaskManagerService 构造,再经过 PackageManager 解析后补充成显式 Intent。

startHomeOnTaskDisplayArea 会先调用 getHomeIntent() 拿到一个 homeIntent 再传入 resolveHomeActivity() 拿到 aInfo

/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
// line 1346-1347
    boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
            boolean allowInstrumenting, boolean fromHomeKey) {
// line 1363-1370
        Intent homeIntent = null;
        ActivityInfo aInfo = null;
        if (taskDisplayArea == getDefaultTaskDisplayArea()
                || mWmService.shouldPlacePrimaryHomeOnDisplay(
                        taskDisplayArea.getDisplayId(), userId)) {
            homeIntent = mService.getHomeIntent();
            aInfo = resolveHomeActivity(userId, homeIntent);
        } else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {

首先来看 getHomeIntent()

/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
// line 564-567
    /** Used to control how we initialize the service. */
    ComponentName mTopComponent;
    String mTopAction = Intent.ACTION_MAIN;
    String mTopData;
// line 5609-5617
    Intent getHomeIntent() {
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }

构建了一个带有 Home 匹配条件的隐式 Intent

Text Only
1
2
3
4
5
Intent {
    action    = android.intent.action.MAIN
    category  = android.intent.category.HOME
    component = null
}

然后来看 resolveHomeActivity()

/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
// line 1413-1443
    ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
        final ComponentName comp = homeIntent.getComponent();
        ActivityInfo aInfo = null;
        try {
            if (comp != null) {
                // Factory test.
                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
            } else {
                final String resolvedType =
                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
                final ResolveInfo info = mTaskSupervisor.resolveIntent(homeIntent, resolvedType,
                        userId, flags, Binder.getCallingUid(), Binder.getCallingPid());
                if (info != null) {
                    aInfo = info.activityInfo;
                }
            }
        } catch (RemoteException e) {
            // ignore
        }

        if (aInfo == null) {
            Slogf.wtf(TAG, new Exception(), "No home screen found for %s and user %d", homeIntent,
                    userId);
            return null;
        }

        aInfo = new ActivityInfo(aInfo);
        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
        return aInfo;
    }


        aInfo = new ActivityInfo(aInfo);
        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
        return aInfo;
    }

前面 getHomeIntent() 没有指定 Component,所以会进入隐式 Intent 的解析分支 resolveIntent(),这是一个通用的 Activity Intent 解析入口。它根据 Intent 中的 action、category、data、MIME type、package、component 等约束,在指定用户下找出候选 Activity,并最终返回一个“最佳匹配”的 ResolveInfo,具体细节就不在此深入了。

aosp 中默认桌面是 Launcher3,那么解析结果应该对应为 aInfo.applicationInfo.packageName = com.android.launcher3 aInfo.name = com.android.launcher3.Launcher

在正式调用 startHomeActivity() 之前,startHomeOnTaskDisplayArea() 会把解析到的 Launcher 组件写回同一个 Intent:

/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
// line 1346-1347
    boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
            boolean allowInstrumenting, boolean fromHomeKey) {
// line 1389-1403
        // Updates the home component of the intent.
        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
        // Updates the extra information of the intent.
        if (fromHomeKey) {
            homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
        }
        homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);

        // Update the reason for ANR debugging to verify if the user activity is the one that
        // actually launched.
        final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
                aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();
        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                taskDisplayArea);

此时 homeIntent 从隐式 Intent 变成了显式 Intent:

Text Only
1
2
3
4
5
Intent {
    action    = android.intent.action.MAIN
    category  = android.intent.category.HOME
    component = com.android.launcher3/.Launcher
}

得到显式的 Home Intent 后,进入 ActivityStartController

/frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java
// line 167-185
    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,
                           TaskDisplayArea taskDisplayArea) {
        if (mHomeLaunchingTaskDisplayAreas.contains(taskDisplayArea)) {
            Slog.e(TAG, "Abort starting home on " + taskDisplayArea + " recursively.");
            return;
        }

        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
        if (!ActivityRecord.isResolverActivity(aInfo.name)) {
            // The resolver activity shouldn't be put in root home task because when the
            // foreground is standard type activity, the resolver activity should be put on the
            // top of current foreground instead of bring root home task to front.
            options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
        }
        final int displayId = taskDisplayArea.getDisplayId();
        options.setLaunchDisplayId(displayId);
        options.setLaunchTaskDisplayArea(taskDisplayArea.mRemoteToken
                .toWindowContainerToken());

        // The home activity will be started later, defer resuming to avoid unnecessary operations
        // (e.g. start home recursively) when creating root home task.
        mSupervisor.beginDeferResume();
        final Task rootHomeTask;
        try {
            // Make sure root home task exists on display area.
            rootHomeTask = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
        } finally {
            mSupervisor.endDeferResume();
        }

        try {
            mHomeLaunchingTaskDisplayAreas.add(taskDisplayArea);
            mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                    .setOutActivity(tmpOutRecord)
                    .setCallingUid(0)
                    .setActivityInfo(aInfo)
                    .setActivityOptions(options.toBundle(),
                            Binder.getCallingPid(), Binder.getCallingUid())
                    .execute();

这里为启动设定了一些参数,然后通过 obtainStarter().execute() 进入通用的应用启动流程

这里我们先不继续深入,而是假装桌面已经启动完毕,我们先来看看桌面是如何处理用户点击,系统又是如何再一次的走到这里,最后我们在随着普通应用的启动流程再深入这套通用流程后续操作

2. 从点击到应用启动