Android 窗口结构(三) Home Task 添加Home ActivityRecord

Android系统起来之后,会启动Launcher的主Activity。

看一下调用链

SystemServer startOtherServices(......)

--> ActivityManagerService systemReady(......)

--> LocalService startHomeOnAllDisplays(......) LocalService是ActivityTaskManagerService 类的内部类

-> RootWindowContainer startHomeOnAllDisplays(......)

-> RootWindowContainer startHomeOnAllDisplays(int userId, String reason, int displayId)

--> RootWindowContainer startHomeOnAllDisplays(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey)

--> DisplayContent reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->

result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea, allowInstrumenting, fromHomeKey),

false /* initValue */)

--> RootWindowContainer startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey)

--> ActivityStartController. startHomeActivity(......)

--> ActivityStarter execute()

这里直接从RootWindowContainer startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowInstrumenting, boolean fromHomeKey)看,代码如下:

java 复制代码
    boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
            boolean allowInstrumenting, boolean fromHomeKey) {
        // Fallback to top focused display area if the provided one is invalid.
        if (taskDisplayArea == null) {
            final Task rootTask = getTopDisplayFocusedRootTask();
            taskDisplayArea = rootTask != null ? rootTask.getDisplayArea()
                    : getDefaultTaskDisplayArea();
        }

        Intent homeIntent = null;
        ActivityInfo aInfo = null;
        if (taskDisplayArea == getDefaultTaskDisplayArea()) {
            homeIntent = mService.getHomeIntent();
            aInfo = resolveHomeActivity(userId, homeIntent);
        } else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
            Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, taskDisplayArea);
            aInfo = info.first;
            homeIntent = info.second;
        }
        if (aInfo == null || homeIntent == null) {
            return false;
        }

        if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, allowInstrumenting)) {
            return false;
        }

        // 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);
            if (mWindowManager.getRecentsAnimationController() != null) {
                mWindowManager.getRecentsAnimationController().cancelAnimationForHomeStart();
            }
        }
        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);
        return true;
    }

taskDisplayArea是不为null的,它的构建可以参考看一下这篇文章 Android 窗口结构(一) 窗口层级构造

Android是支持多屏幕的,这里咱们针对单屏幕来说。也即是满足第一个if分支的情况。下面是得到homeIntent,然后通过homeIntent再得到aInfo。

这里mService是ActivityTaskManagerService对象,它的getHomeIntent()得到的Intent对象,action为Intent.ACTION_MAIN,Category为Intent.CATEGORY_HOME。这个是Launcher的主Activity配置。

接着通过resolveHomeActivity(userId, homeIntent)解析该Intent对象,得到对应的Activity信息对象aInfo。这块如果只存在一个Launcher,它就返回对应Launcher主Activity的信息;如果是配置了多个Launcher,它会返回ResolverActivity的信息,如果是这样,等下面启动ResolverActivity时,它会显示一个界面 ,让选择运行哪个Launcher。这里咱们就说一个Launcher的情况。

接下来,它会配置Intent对象的ComponentName。它包括包名和Activity名字。还会加上FLAG_ACTIVITY_NEW_TASK标识,它代表启动一个新的Task。

再接下来就是调用ActivityStartController类对象startHomeActivity()方法去启动Activity。mService.getActivityStartController()是ActivityTaskManagerService对象的成员mActivityStartController,它是ActivityStartController类型。

ActivityStartController类的startHomeActivity方法如下:

java 复制代码
    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,
            TaskDisplayArea taskDisplayArea) {
        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();
        }

        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                .setOutActivity(tmpOutRecord)
                .setCallingUid(0)
                .setActivityInfo(aInfo)
                .setActivityOptions(options.toBundle())
                .execute();
        mLastHomeActivityStartRecord = tmpOutRecord[0];
        if (rootHomeTask.mInResumeTopActivity) {
            // If we are in resume section already, home activity will be initialized, but not
            // resumed (to avoid recursive resume) and will stay that way until something pokes it
            // again. We need to schedule another resume.
            mSupervisor.scheduleResumeTopActivities();
        }
    }

首先设置参数ActivityOptions类型options,先设置窗口模式全屏(WINDOWING_MODE_FULLSCREEN),如果启动的Activity不是ResolverActivity,设置启动Activty类型为ACTIVITY_TYPE_HOME,设置显示屏Id。

taskDisplayArea.mRemoteToken是RemoteToken类型对象,它继承自IWindowContainerToken.Stub,用来实现Binder通信。taskDisplayArea.mRemoteToken.toWindowContainerToken()则是WindowContainerToken(实现Parcelable接口,用来序列化)对象。这里也将它设置到options中。

再接下来就从taskDisplayArea中获取RootHomeTask。因为RootHomeTask的初始化在DisplayContent初始化中,DisplayContent初始化是在RootWindowContainer的setWindowManager(WindowManagerService wm)中,它又在ActivityManagerService的setWindowManager(WindowManagerService wm)中,所以现在它已经创建了。创建的过程参考 Android 窗口结构(二) 添加Home Task

obtainStarter(intent, "startHomeActivity: " + reason)得到的是ActivityStarter对象,接着会向其设置intent对象,还会调用setCallingUid(0)、setActivityInfo(aInfo)、setActivityOptions(options.toBundle())分别设置调用Uid,启动的Activity信息、设置参数。最后调用ActivityStarter对象的execute()方法,执行。

tmpOutRecord是一个数组,是一个输出参数,所以上述方法调用完毕,之后,将它设置到mLastHomeActivityStartRecord中,作为最后一次启动的Home Activity。

如果rootHomeTask.mInResumeTopActivity为true,代表Home Task已经在执行恢复Activity的操作了,导致现在的Home Activity没有执行resume。所以这里接着调用mSupervisor.scheduleResumeTopActivities()继续执行Home Activity的resume。

下面进入ActivityStarter类中看一下它的execute()方法,如下:

java 复制代码
    /**
     * Resolve necessary information according the request parameters provided earlier, and execute
     * the request which begin the journey of starting an activity.
     * @return The starter result.
     */
    int execute() {
        try {
            // Refuse possible leaked file descriptors
            if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {
                throw new IllegalArgumentException("File descriptors passed in Intent");
            }

            final LaunchingState launchingState;
            synchronized (mService.mGlobalLock) {
                final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);
                final int callingUid = mRequest.realCallingUid == Request.DEFAULT_REAL_CALLING_UID
                        ?  Binder.getCallingUid() : mRequest.realCallingUid;
                launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(
                        mRequest.intent, caller, callingUid);
            }

            // If the caller hasn't already resolved the activity, we're willing
            // to do so here. If the caller is already holding the WM lock here,
            // and we need to check dynamic Uri permissions, then we're forced
            // to assume those permissions are denied to avoid deadlocking.
            if (mRequest.activityInfo == null) {
                mRequest.resolveActivity(mSupervisor);
            }

            // Add checkpoint for this shutdown or reboot attempt, so we can record the original
            // intent action and package name.
            if (mRequest.intent != null) {
                String intentAction = mRequest.intent.getAction();
                String callingPackage = mRequest.callingPackage;
                if (intentAction != null && callingPackage != null
                        && (Intent.ACTION_REQUEST_SHUTDOWN.equals(intentAction)
                                || Intent.ACTION_SHUTDOWN.equals(intentAction)
                                || Intent.ACTION_REBOOT.equals(intentAction))) {
                    ShutdownCheckPoints.recordCheckPoint(intentAction, callingPackage, null);
                }
            }

            int res;
            synchronized (mService.mGlobalLock) {
                final boolean globalConfigWillChange = mRequest.globalConfig != null
                        && mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
                final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
                if (rootTask != null) {
                    rootTask.mConfigWillChange = globalConfigWillChange;
                }
                ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "
                        + "will change = %b", globalConfigWillChange);

                final long origId = Binder.clearCallingIdentity();

                res = resolveToHeavyWeightSwitcherIfNeeded();
                if (res != START_SUCCESS) {
                    return res;
                }
                res = executeRequest(mRequest);

                Binder.restoreCallingIdentity(origId);

                if (globalConfigWillChange) {
                    // If the caller also wants to switch to a new configuration, do so now.
                    // This allows a clean switch, as we are waiting for the current activity
                    // to pause (so we will not destroy it), and have not yet started the
                    // next activity.
                    mService.mAmInternal.enforceCallingPermission(
                            android.Manifest.permission.CHANGE_CONFIGURATION,
                            "updateConfiguration()");
                    if (rootTask != null) {
                        rootTask.mConfigWillChange = false;
                    }
                    ProtoLog.v(WM_DEBUG_CONFIGURATION,
                                "Updating to new configuration after starting activity.");

                    mService.updateConfigurationLocked(mRequest.globalConfig, null, false);
                }

                // The original options may have additional info about metrics. The mOptions is not
                // used here because it may be cleared in setTargetRootTaskIfNeeded.
                final ActivityOptions originalOptions = mRequest.activityOptions != null
                        ? mRequest.activityOptions.getOriginalOptions() : null;
                // If the new record is the one that started, a new activity has created.
                final boolean newActivityCreated = mStartActivity == mLastStartActivityRecord;
                // Notify ActivityMetricsLogger that the activity has launched.
                // ActivityMetricsLogger will then wait for the windows to be drawn and populate
                // WaitResult.
                mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
                        newActivityCreated, mLastStartActivityRecord, originalOptions);
                if (mRequest.waitResult != null) {
                    mRequest.waitResult.result = res;
                    res = waitResultIfNeeded(mRequest.waitResult, mLastStartActivityRecord,
                            launchingState);
                }
                return getExternalResult(res);
            }
        } finally {
            onExecutionComplete();
        }
    }

如果Intent中含有文件描述符,是会抛出IllegalArgumentException异常的。

mSupervisor.getActivityMetricsLogger()是ActivityMetricsLogger对象,这个类是用来监听Activity启动、过渡、可见性改变、窗口绘制进行回调的。它的notifyActivityLaunching()就是通知Activity启动了。在这里它会记录启动的时间。

如果mRequest.activityInfo == null,会需要解析它,这里前面已经设置它了,不需要解析它。

如果intentAction和调用的包名callingPackage不为null,并且是关机或者重启的请求,会添加检查点。

变量globalConfigWillChange是全局的配置是否发生了变化,是用mRequest.globalConfig(不能为null)和mService.getGlobalConfiguration()进行比较,如果不同即发生了变化。这里因为没有设置mRequest.globalConfig,所以它为null。globalConfigWillChange也为false。

mRootWindowContainer.getTopDisplayFocusedRootTask()里面目前也就只有一个Home Task。所以这里就得到它。

resolveToHeavyWeightSwitcherIfNeeded()是在必要的情况下(需要满足配置特色条件),在请求的进程uid和当前的重量级进程不同的情况下,需要跳转到HeavyWeightSwitcherActivity中去操作处理。目前这个暂时忽略。

executeRequest(mRequest)就是去执行启动Activity的操作。这个可以参考 Android Activity的启动器ActivityStarter入口。其中Home ActivityRecord添加到Task中,也是由它实现的。这里,它主要是由addOrReparentStartingActivity方法实现的。

下面globalConfigWillChange为false,所以不用更新配置。

mStartActivity、mLastStartActivityRecord都是在executeRequest(mRequest)中赋值的,只不过赋值的地方不同。mLastStartActivityRecord是在ActivityRecord对象刚创建的时候设置的,而mStartActivity是在启动Activity时设置的,所以它俩一致,代表Activity创建成功,所以newActivityCreated为true。

接下来就是调用ActivityMetricsLogger对象的notifyActivityLaunched方法,通知Activity已经启动完。

如果设置了mRequest.waitResult,则需要对它进行处理。并将它的处理结果返回给res。

getExternalResult(res)是在res为START_ABORTED时,也返回START_SUCCESS。

onExecutionComplete()主要是处理启动器类ActivityStarter对象的回收。

相关推荐
这儿有一堆花2 小时前
PHP文件与本地及外部资源的深度交互指南
开发语言·php
灿烂阳光g2 小时前
App进程是如何从Zygote中fork出来的
android
不良人天码星2 小时前
使用Java连接redis以及开放redis端口的问题
java·开发语言·redis
谢语花2 小时前
【VS2022】LNK assimp64.lib找不到文件_openframework
android·运维·服务器
model20052 小时前
Android 配置多个 cmake
android
恸流失3 小时前
java基础-12 : 单列集合(Collection)
java·开发语言·windows
sun03223 小时前
工作中使用到的单词(软件开发)_第五版
开发语言·软件开放单词
做运维的阿瑞3 小时前
告别性能焦虑:Python 性能革命实践指南
开发语言·后端·python
教程分享大师3 小时前
【升级安卓9教程】华为Q21_Q21A_Q21C_Q21AQ_Q21AE_hi3798mv200线刷烧录包带adb权限
android·adb