【Android 14源码分析】Activity启动流程-1

news2024/11/19 11:29:29

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
                  
                  
                                           – 服装学院的IT男

本篇已收录于Activity短暂的一生系列
欢迎一起学习讨论Android应用开发或者WMS
V:WJB6995
Q:707409815

正文

由于篇幅原因,整个启动流程分为以下3篇进行分析:

Activity启动流程-1

Activity启动流程-2

Activity启动流程-3

Activity 的启动流程无论是 APP 开发还是 FrameWork 开发都是必须要熟悉的一个流程。

FrameWork 中有很多重要的流程都是在 Activity 启动过程中触发的,不过当前还是以分析 Activity 启动主流程为主,当然对于一些关键方法还是会着重介绍,这样以后如果有遇到相关问题的修改可以通过这篇文章找到具体代码位置,然后根据具体的问题分析修改。

启动 Activity 的方式有很多,当前以在 Launcher 点击“电话”图标启动应用场景进行分析。

1. 整体介绍

1.1 一级框图

在这里插入图片描述
对于Activity的启动流程来说,可以分为3个模块:

SourceActivity:执行 startActivity 方法的 Activity ,也就是发起请求的Activity ,当前是 Launcher 的 Activity 。

TargetActivity:被启动的 Activity 当前就是“电话”应用在清单文件配置的MainActivity

AMS: 不仅仅是指 AMS 这一个类,而是指在这个过程中 system_service 进程参与处理的相关类

举个例子,以在公司的工作流程来说, Launcher 模块的开发,在处理一个 bug,但是涉及到了通话,那么他需要找到通讯组的同事来处理这个问题。但公司很大,他并不知道通讯模块是谁负责,更不知道这个问题需要交给通讯组具体的哪个同事处理,那么他只需要将自己的要求向公司领导(管理着)汇报:需要通讯组的同事处理这个问题。

当前例子设计到 launcher 和通讯2个模块的开发人员,还涉及到公司的管理者。在Activity 启动也是如此, 对于 SourceActivity、TargetActivity 来说他们并不知道对方模块的业务,所以这一流程需要AMS来做管理。

并且,这3个模块也对应3个进程,当前案例来说分别为:Launcher 进程,电话进程,system_service 进程。

图中AMS 对 Launcher 多了一个返回箭头的原因是 Launcher 肯定是需要执行 pause 的,但执行 pause 的时机 Launcher 自身无法控制,只能由 AMS 控制。

1.2 总体流程

在这里插入图片描述
这里有4个阶段,以4个颜色表示,同时还涉及到3个进程。

第一阶段:

    1. 由 Launcher 进程发起启动 Activity 的请求
    1. AMS 处理,创建对应的 ActivityRecord 和 Task ,并挂载到窗口层级树中
    1. AMS 触发 Launcher 的 pause 流程
    1. AMS 触发"电话"应用进程的创建

第二阶段:

    1. Launcher 执行完 pause
    1. pause 流程完成后触发 AMS 启动 TargetActivity

第三阶段:

    1. 创建应用进程
    1. 应用进程创建好后会向 AMS 进行绑定,并触发 AMS 启动 TargetActivity

第四阶段:

    1. AMS 触发 realStartActivityLocked 方法试图应用启动 Activity
    1. 如果还有 Activity 没有执行完 pause 逻辑,则 realStartActivityLocked 会 return
    1. 如果应用 Activity 都执行完了 pause ,则触发 TargetActivity 的启动,并将生命周期会执行到 onResume

注意:其中第2,3阶段几乎是同时开始的,并且 ASM 通知 Launcher 执行 pause 和通过 Zygote 创建进程是异步操作,不知道各自执行的顺序。

但是看的出来最终都是会执行到 realStartActivityLocked 方法来试图启动 TargetActivity ,为什么说是“试图启动”启动呢?

一共就有2种可能:

    1. completePause 先执行完
    1. 进程先创建完

假设第一种情况:
Launcher 先执行完 completePause 来到 ActivityTaskSupervisor::startSpecificActivity 方法,这个时候进程还没创建完毕,则不会执行 ActivityTaskSupervisor::realStartActivityLocked ,而是会再触发进程创建,当然之前已经触发过来,这次触发不会真的再创建进行了。

然后会由阶段三的流程创建好进程走到 ActivityTaskSupervisor::realStartActivityLocked ,在这个方法里会判断是不是执行完 pause 了,那当前这个情况肯定是满足的,
所以会触发启动 TargetActivity 。

假设第二种情况:
阶段三创建进程先执行完,走到 ActivityTaskSupervisor::realStartActivityLocked 方法,但是发现 pause 还没执行玩,所以就 return 了。
然后等 completePause 流程来到 ActivityTaskSupervisor::startSpecificActivity 方法时,这次发现进程已经创建好了,则一次执行后续逻辑触发启动 TargetActivity 。

也就是说不管阶段二,三谁先执行完都会试图启动 TargetActivity ,而成功启动必须有2个条件:

    1. 进程创建完毕
    1. launcher执行完pause

也就说说这2个阶段后执行过来的流程才能正式启动 TargetActivity 。

2. 阶段一:桌面点击图标启动应用

2.1 流程概览

这一阶段调用比较简单,堆栈如下:
在这里插入图片描述
整理后如下:

ItemClickHandler::onClick
    ItemClickHandler::onClickAppShortcut
        ItemClickHandler::startAppShortcutOrInfoActivity
            QuickstepLauncher::startActivitySafely
                Launcher::startActivitySafely
                    AppLauncher::startActivitySafely
                        BaseQuickstepLauncher::getActivityLaunchOptions    -- 构建 Option 参数
                        Activity::startActivity  -- 共用启动Activity流程
                            Activity::startActivity
                                Activity::startActivity
                                    Activity::startActivityForResult
                                        Instrumentation::execStartActivity
                                            ActivityTaskManagerService::startActivity -- 跨进程

画成时序图:
在这里插入图片描述

其实我们正常通过 startActivity 传递 Intent 启动 Activity 的流程也是一样的,最终都会调到 Instrumentation::execStartActivity 然后开始跨进程与 AMS 通信。
只不过这边多了一些 Launcher 自己的处理,这边只需要对 BaseQuickstepLauncher::getActivityLaunchOptions 有个印象即可,这个方法会构建 ActivityOptions 对象,包含了一些启动参数,比如:远端动画的 RemoteAnimationAdapter 。

前面的流程就不看了,执行从 Activity::startActivityForResult 开始看代码流程。

2.2 SourceActivity 端处理

SourceActivity 发起启动 Activity 的逻辑相对简单,无论哪种参数的 Activity::startActivity 方法,最终都是调到 Activity::startActivityForResult 方法。
然后在 Instrumentation 最后的处理,最后跨进程传递到 system_service 进程中。

# Activity.java
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            ......
        }
        ......
    }
# Instrumentation.java
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ......
            // 当前应用进程处理结束,开始传递给 ActivityTaskManagerService
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getOpPackageName(), who.getAttributionTag(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
            // 对跨进程启动的结果做check
            checkStartActivityResult(result, intent);
        ......
    }

Instrumentation::checkStartActivityResult 这个方法有一些常见的报错,比如常见的未在 AndroidManifest.xml 注册 Activity 的报错就在这。

# Instrumentation

    public static void checkStartActivityResult(int res, Object intent) {
        if (!ActivityManager.isStartResultFatalError(res)) {
            return;
        }

        switch (res) {
            case ActivityManager.START_INTENT_NOT_RESOLVED:
            case ActivityManager.START_CLASS_NOT_FOUND:  // 未在AndroidManifest.xml注册
                if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
                    throw new ActivityNotFoundException(
                            "Unable to find explicit activity class "
                            + ((Intent)intent).getComponent().toShortString()
                            + "; have you declared this activity in your AndroidManifest.xml"
                            + ", or does your intent not match its declared <intent-filter>?");
                throw new ActivityNotFoundException(
                        "No Activity found to handle " + intent);
            case ActivityManager.START_PERMISSION_DENIED:
                throw new SecurityException("Not allowed to start activity "
                        + intent);
            case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
                throw new AndroidRuntimeException(
                        "FORWARD_RESULT_FLAG used while also requesting a result");
            case ActivityManager.START_NOT_ACTIVITY:
                throw new IllegalArgumentException(
                        "PendingIntent is not an activity");
            case ActivityManager.START_NOT_VOICE_COMPATIBLE:
                throw new SecurityException(
                        "Starting under voice control not allowed for: " + intent);
            case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
                throw new IllegalStateException(
                        "Session calling startVoiceActivity does not match active session");
            case ActivityManager.START_VOICE_HIDDEN_SESSION:
                throw new IllegalStateException(
                        "Cannot start voice activity on a hidden session");
            case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:
                throw new IllegalStateException(
                        "Session calling startAssistantActivity does not match active session");
            case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:
                throw new IllegalStateException(
                        "Cannot start assistant activity on a hidden session");
            case ActivityManager.START_CANCELED:
                throw new AndroidRuntimeException("Activity could not be started for "
                        + intent);
            default:
                throw new AndroidRuntimeException("Unknown error code "
                        + res + " when starting " + intent);
        }
    }

3. 阶段一 : system_service 处理

3.1 流程概述

对于 system_service 来说,它收到了启动 Activity 的请求,那么它要做的就是启动 TargetActivity 。

要实现这个目的,我整理了大概要做以下几件事:

    1. 解析请求参数
    • 想要启动 Activity 最起码得知道具体是哪个 Activity 吧,当然还有其他很多参数,比如是谁发起的请求,requestCode等等,这些参数都是应用端带过来的。system_service 的第一件事肯定是解析这些参数,这些参数将被解析存放在一个叫 Request 对象中。
    1. 处理窗口层级树相关逻辑
    • 虽然是启动 Activity ,但是肯定涉及到窗口操作,所以这一步也是必须的
    1. 触发 SourceActivity 执行pause 逻辑
    • 需要显示新的 TargetActivity ,那之前的 SourceActivity 肯定要执行 pause 逻辑的
    1. 触发创建进程
    • 当前场景是冷启动,那启动 TargetActivity 前必须要保证其所在的进程已经存在

在 Instrumentation::execStartActivity 里就开始触发跨进程通信了,剩下的逻辑就在 system_service 进程中执行了。

这部分缩略版的的调用链如下:

ActivityTaskManagerService::startActivity
    ActivityTaskManagerService::startActivityAsUser
        ActivityTaskManagerService::startActivityAsUser
            ActivityStartController::obtainStarter
                ActivityStarter::execute
                    ActivityStarter$Request::resolveActivity           -- 解析启动请求参数
                    ActivityStarter::executeRequest                    -- 3.3 创建ActivityRecord
                        ActivityStarter::startActivityUnchecked
                            ActivityStarter::startActivityInner        -- 3.4 关键函数startActivityInner
                                ActivityStarter::getOrCreateRootTask   -- 3.4.1 创建或者拿到Task                
                                ActivityStarter::setNewTask            -- 3.4.2 将Task与activityRecord 绑定
                                Task::moveToFront                       --3.4.3 移动Task到栈顶
                                RootWindowContainer::resumeFocusedTasksTopActivities    --3.4.4 显示Activity
                                    Task::resumeTopActivityUncheckedLocked
                                        Task::resumeTopActivityInnerLocked
                                            TaskFragment::resumeTopActivity      --  显示顶层Activity
                                                TaskDisplayArea::pauseBackTasks                   -- pause LauncherActivity 
                                                ActivityTaskManagerService::startProcessAsync     -- 创建“电话”进程

时序图:
在这里插入图片描述

流程来到 ActivityTaskManagerService::startActivity ,经过2次简单的跳转会执行 ActivityTaskManagerService::startActivityAsUser 方法。
这个方法比较重要,在这里会构建一个 ActivityStartController ,根据类名可以知道这个类是控制 Activity 启动。

3.2 启动请求参数的构建

代码如下:

# ActivityTaskManagerService

    private int startActivityAsUser(IApplicationThread caller, String callingPackage,
            @Nullable String callingFeatureId, Intent intent, String resolvedType,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
            ......
            // 返回的是ActivityStartController
            return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setCallingFeatureId(callingFeatureId)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setUserId(userId)
                .execute();
    }

    ActivityStartController getActivityStartController() {
        return mActivityStartController;
    }

ActivityStartController::obtainStarter 返回的是 ActivityStarter 对象。

而 ActivityStarter 这个类名看着就是处理启动 Activity 的一个类,它做的是就是解析调用者传递的参数,构建出一个 Request 然后开始执行后续的启动 Activity 逻辑。

# ActivityStartController
    // 返回ActivityStarter
    ActivityStarter obtainStarter(Intent intent, String reason) {
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }

所以在 ActivityTaskManagerService::startActivityAsUser 方法中的 build 模式,其实是对 ActivityStarter 对象做构建,最终调用其 execute 方法。
在内容最终执行了 ActivityStarter::executeRequest 方法,下面只以 setCallingPackage 这一个方法看一下是怎么把参数设置到 Request 中的。

# ActivityStarter
    // 启动Activity的请求
    Request mRequest = new Request();
    // 设置调用者包名
    ActivityStarter setCallingPackage(String callingPackage) {
        mRequest.callingPackage = callingPackage;
        return this;
    }
    // 设置ActivityInfo
    ActivityStarter setActivityInfo(ActivityInfo info) {
        mRequest.activityInfo = info;
        return this;
    }
    int execute() {
        ......
        // 如果 mRequest 中没有 Activity 相关信息
        if (mRequest.activityInfo == null) {
            // 解析请求数据
            mRequest.resolveActivity(mSupervisor);
        }
        ......
        // 执行启动Activity请求
        res = executeRequest(mRequest);
        ......
    }

build 模式会把参数设置到 mRequest 中,但是并没有看到调用 ActivityStarter::setActivityInfo 所以在执行 ActivityStarter::execute 的时候 “mRequest.activityInfo”是 null 。
所以这里又分2步:

    1. 解析获取 activityInfo
    1. 执行后续启动逻辑

3.2.1 activityInfo 的解析

ActivityStarter$Request

    ResolveInfo resolveInfo;
    ActivityInfo activityInfo;

    void resolveActivity(ActivityTaskSupervisor supervisor) {
        ......
            // 解析resolveInfo
            resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
                0 /* matchFlags */,
                computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid),
                realCallingPid);
            ......
            // 解析activityInfo 
            activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
                    profilerInfo);
            ......
    }

TargetActivity 所在的进程包名和 TargetActivity 的完整路径都在 activityInfo 中,这个解析方法也是一个核心点,当前分析主流程就不细看了。

现在 mRequest 中就包含了启动 TargetActivity 的所有数据,就可以继续后面的启动流程了。

3.3 创建ActivityRecord

ActivityStarter::executeRequest 是一个需要注意的方法,因为内部会创建 ActivityRecord 对象,而这个 ActivityRecord 对象持有 token ,这个 token 就是以后分析其他逻辑一直会出现的 token 。也可以说是 system_service 中一个 Activity 的唯一标识。

应用进程中的 Activity 在 AMS 的代表就是 ActivityRecord 。

# ActivityStarter

  private int executeRequest(Request request) {
        ......
        // 包名在这
        ActivityInfo aInfo = request.activityInfo;
        ResolveInfo rInfo = request.resolveInfo;
        ......
        if (err == ActivityManager.START_SUCCESS) {
            // 重点* 1. 打印创建 ActivityRecord 日志
            request.logMessage.append("START u").append(userId).append(" {")
                    .append(intent.toShortString(true, true, true, false))
                    .append("} with ").append(launchModeToString(launchMode))
                    .append(" from uid ").append(callingUid);
            if (callingUid != realCallingUid
                    && realCallingUid != Request.DEFAULT_REAL_CALLING_UID) {
                request.logMessage.append(" (realCallingUid=").append(realCallingUid).append(")");
            }
        }
        ......
        // 重点* 2. 构造出一个ActivityRecord
        final ActivityRecord r = new ActivityRecord.Builder(mService)
                .setCaller(callerApp)
                .setLaunchedFromPid(callingPid)
                .setLaunchedFromUid(callingUid)
                .setLaunchedFromPackage(callingPackage)
                .setLaunchedFromFeature(callingFeatureId)
                .setIntent(intent)
                .setResolvedType(resolvedType)
                .setActivityInfo(aInfo)
                .setConfiguration(mService.getGlobalConfiguration())
                .setResultTo(resultRecord)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setComponentSpecified(request.componentSpecified)
                .setRootVoiceInteraction(voiceSession != null)
                .setActivityOptions(checkedOptions)
                .setSourceRecord(sourceRecord)
                .build();
        ......
        // 重点* 3. 继续执行startActivityUnchecked
        mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,
                inTask, inTaskFragment, restrictedBgActivity, intentGrants);
        ......
  }

3个注意点:

    1. 虽然是个日志,但是也很关键,一般分析日志看启动了哪个 Activity 就搜“START u” ,打印的地方就在这。另外发现 U 版本上这一块还做了改变。
    1. 构建出一个 ActivityRecord 对象
    1. 继续主流程
      主要看第二点 ActivityRecord 是怎么创建的。
# ActivityRecord
    private ActivityRecord(ActivityTaskManagerService _service, ......) {
            // 调用父类,创建 Token
            super(_service.mWindowManager, new Token(), TYPE_APPLICATION, true,
                null /* displayContent */, false /* ownerCanManageAppTokens */);
            ......
            packageName = info.applicationInfo.packageName;
            .....
        }

这里的重点其实就是创建了一个匿名 Token 然后传递到了父类的构建方法, ActivityRecord 的父类是 WindowToken 。

# WindowToken

    protected WindowToken(WindowManagerService service, IBinder _token, int type,
            boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) {
        this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
                false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */);
    }

    protected WindowToken(WindowManagerService service, IBinder _token, int type,
            boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
            boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
        super(service);
        // token 赋值
        token = _token;
        windowType = type;
        mOptions = options;
        ......
        if (dc != null) {
            // 挂载到窗口树,
            dc.addWindowToken(token, this);
        }
    }

将 ActivityRecord 创建的匿名 保存在了 token 对象,这个 token 就是 Activity 在 system_service 里唯一标识符。

普通的 WindowToken 在这里会挂载的窗口层级树,但是 DisplayContent::addWindowToken 方法内部对 ActivityRecord 做了判断。也就是说 ActivityRecord 刚创建好后是不会挂载到窗口树上的,当前流程后面是要介绍 ActivityRecord 是怎么挂载到层级树上的。

ActivityRecord 创建好后继续走主流程,根据前面的分析下一步是执行 ActivityStarter::startActivityUnchecked 不过这个方法也没做啥,主要是调用了 ActivityStarter::startActivityInner ,这个方法是流程的关键点。

3.4 窗口层级树处理

ActivityStarter::startActivityInner 是 Activity 启动流程最重要的函数之一,这里涉及到【窗口层级树】相关知识,上一小节说了 ActivityRecord 创建好后并不会和 WindowToken 一样挂载到层级树中,而是需要单独处理,本小节就是看这一块是如何处理的。

先看看正常在 Launcher 界面时和启动“电话”后的层级树对比。

在这里插入图片描述

这里首先多了3个东西:

    1. Task
    1. 上一步创建的 ActivityRecord
    1. WindowState。

另外这个 Task 还移动到了DefaultTaskDisplayArea的最顶部,这里涉及到的操作如下:

    1. 创建 Task
    1. ActivityRecord 挂在到这个 Task 下
    1. 将这个 Task 移动到最上面

至于最下面的那个 546fce2 为什么是 WindowState 对象,又是怎么挂在到 ActivityRecord 上的,这个属于窗口显示第一步-addWindow流程当前就不单独分析了。

继续看主流程代码:

# ActivityStarter

    // The task that the last activity was started into. We currently reset the actual start
    // activity's task and as a result may not have a reference to the task in all cases
    private Task mTargetTask;

    int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, boolean restrictedBgActivity,
            NeededUriGrants intentGrants) {
            ......
            // computeTargetTask内部会根据具体条件返回Task(比如标志位FLAG_ACTIVITY_NEW_TASK 就需要重新创建Task   )
            // 这里reusedTask为 null,因为是新启动的应用,所以computeTargetTask也找不到task,最终也为null
            
            final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
            // 那么newTask为true, 表示需要新建一个task
            final boolean newTask = targetTask == null;
            // 同样为null
            mTargetTask = targetTask;
            ......
            if (mTargetRootTask == null) {
                // 重点* 1. 创建Task   23
                mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
                        mOptions);
            }
            if (newTask) {
                // taskToAffiliate 为null
                final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                        ? mSourceRecord.getTask() : null;
                // 重点* 2. 将需要启动的ActivityRecord与 新创建的Task 进行绑定
                setNewTask(taskToAffiliate);
            } else if (mAddingToTask) {
                addOrReparentStartingActivity(targetTask, "adding to task");
            }

            if (!mAvoidMoveToFront && mDoResume) {
                // 重点* 3. 移动到栈顶 
                mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
                ......
            }
            ......
            if (mDoResume) {
                ......
                // 重点*4. task 处理完后,需要将task顶部的Activity显示(resume) 
                // mTransientLaunch 一般为默认值 false
                mRootWindowContainer.resumeFocusedTasksTopActivities(
                        mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
            }
            ......
        }
    1. 创建 Task
    • 我们知道 ActivityRecord 需要挂载到一个 Task 下,虽然这里调用的方法名是 getOrCreateRootTask(获取或者创建Task),但是当前场景肯定是创建一个,热启动是获取。
    • 另外 Task 创建的时候就会挂载到层级树中,也就是会挂载到DefaultTaskDisplayArea
    1. 将 ActivityRecord 挂载到创建的 Task 下
    • 上一小节没做的事,在这里就完成了挂载
    1. 移动 Task 到栈顶
    • 因为这个新启动的 Activity 需要显示,所以需要将它所在的 Task 移到容器顶部。 不过当前场景建好就已经是栈顶了,其实这一步没做什么,这点后面代码会分析
    1. 显示顶部的 Activity
    • 窗口层级的处理好了,就可以处理显示逻辑了,这里后续的逻辑会触发 Activity 的启动

3.4.1 获取Task–getOrCreateRootTask

调用链

ActivityStarter::getOrCreateRootTask
    RootWindowContainer::getOrCreateRootTask
        RootWindowContainer::getOrCreateRootTask
            TaskDisplayArea::getOrCreateRootTask
                TaskDisplayArea::getOrCreateRootTask
                    Task::Build     ---创建Task

开始看代码:

# ActivityStarter
    private Task getOrCreateRootTask(ActivityRecord r, int launchFlags, Task task,
            ActivityOptions aOptions) {
        final boolean onTop =
                (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
        final Task sourceTask = mSourceRecord != null ? mSourceRecord.getTask() : null;
        return mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, sourceTask, onTop,
                mLaunchParams, launchFlags);
    }

    1. onTop 表示是否要移到到当前栈顶,那肯定是要的,新启动的 Activity 当前要在最上面,这里 aOptions 为 null ,所以为 true
    1. sourceTask 表示从哪里启动的,当前 Launcher 所在的 Task 就是 sourceTask

然后开始获取一个 Task 如果没有就新建一个。

# RootWindowContainer
    Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
            @Nullable Task candidateTask, boolean onTop) {
        return getOrCreateRootTask(r, options, candidateTask, null /* sourceTask */, onTop,
                null /* launchParams */, 0 /* launchFlags */);
    }
    Task getOrCreateRootTask(@Nullable ActivityRecord r,
            @Nullable ActivityOptions options, @Nullable Task candidateTask,
            @Nullable Task sourceTask, boolean onTop,
            @Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {
            ......
            final int activityType = resolveActivityType(r, options, candidateTask);
            if (taskDisplayArea != null) {
                if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {
                    // 重点*1. 传递到TaskDisplayArea
                    return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
                            sourceTask, launchParams, launchFlags, activityType, onTop);
                } else {
                    taskDisplayArea = null;
                }
            }
            ......
    }

经过同名方法调用后,逻辑进入到 TaskDisplayArea::getOrCreateRootTask 。

# TaskDisplayArea
    Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
            @Nullable Task candidateTask, @Nullable Task sourceTask,
            @Nullable ActivityOptions options, int launchFlags) {
            if(....) {
                // 拿到之前创建的Task
                return candidateTask.getRootTask();
            }
            ......// 第一次显示所以是新建Task
            return new Task.Builder(mAtmService)
                .setWindowingMode(windowingMode)
                .setActivityType(activityType)
                .setOnTop(onTop)
                .setParent(this)  // 主要这个this被设置为Parent。所以直接挂载到了DefaultTaskDisplayArea下
                .setSourceTask(sourceTask)
                .setActivityOptions(options)
                .setLaunchFlags(launchFlags)
                .build();
    }

看方法名是获取或创建 Task ,当前流程是新启动的 Activity 所以需要创建 Task 。如果是以默认启动方式打开应用内的另一个 Activity ,就走的是上面的 “return candidateTask.getRootTask();”

设置的 parent 就是层级结构树应用所在的名为“DefaultTaskDisplayArea”的 TaskDisplayArea

接下来就是真正触发Task的创建。

# Task
    # Task.Builder
        Task build() {
            if (mParent != null && mParent instanceof TaskDisplayArea) {
                validateRootTask((TaskDisplayArea) mParent);
            }

            ......
            // 重点* 1. 创建task
            final Task task = buildInner();
            task.mHasBeenVisible = mHasBeenVisible;

            // Set activity type before adding the root task to TaskDisplayArea, so home task can
            // be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().
            if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
                task.setActivityType(mActivityType);
            }
            // 重点* 2. 入栈 这里的 mOnTop 为true
            if (mParent != null) {
                if (mParent instanceof Task) {
                    final Task parentTask = (Task) mParent;
                    parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,
                            (mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
                } else {
                    mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);
                }
            }

            // Set windowing mode after attached to display area or it abort silently.
            if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
                task.setWindowingMode(mWindowingMode, true /* creating */);
            }
            // 返回
            return task;
        }

        // 创建
        Task buildInner() {
            return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,
                    mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,
                    mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,
                    mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,
                    mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,
                    mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
                    mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
                    mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,
                    mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer);
        }
3.4.1.1 小结

最后描述一下最后创建的2个重点部分:

    1. 看到通过 buildInner 创建了一个 Task,而 buildInner 也很简单粗暴,通过各个变量直接 new 出一个 Task 对象。
    1. mParent 不为 null ,是因为在创建的时候 setParent(this),当前的这个 this 就是 getDefaultTaskDisplayArea 返回的。就是 37层的第二层应用 Activity 存在的"DefaultTaskDisplayArea"

在 RootWindowContainer::getOrCreateRootTask 体现。

在这里插入图片描述
注意 log 里的 #17 的这个 Task 与前面的层级结构树新增的 Task 是对应的上的。而且"this= DefaultTaskDisplayArea "说明也确实是往 DefaultTaskDisplayArea 里添加了。
另外 log 里 index 为3,结合最上面的前后对比,说明也的往顶部添加。

3.4.2 ActivityRecord挂载到Task–setNewTask

调用链

    ActivityStarter::setNewTask
        ActivityStarer::addOrReparentStartingActivity

主流程代码

# ActivityStarer

    private void setNewTask(Task taskToAffiliate) {
        // 为true
        final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
        // 就是mTargetRootTask,也就是刚刚创建的Task
        final Task task = mTargetRootTask.reuseOrCreateTask(
                mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
        task.mTransitionController.collectExistenceChange(task);
        // ActivityRecord的挂载
        addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");
        // 需要注意这里的日志打印
        ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",
                mStartActivity, mStartActivity.getTask());

        // mLaunchTaskBehind 为false,所以taskToAffiliate 为null 
        if (taskToAffiliate != null) {
            mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
        }
    }

这里的 Task 和 mTargetRootTask 是同一个对象,然后进入 ActivityStarer::addOrReparentStartingActivity 。

# ActivityStarer

    private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
        //  newParent = task 都是刚刚创建的Task
        TaskFragment newParent = task;
        ......
        if (mStartActivity.getTaskFragment() == null
                || mStartActivity.getTaskFragment() == newParent) {
            // 重点, 将 ActivityRecord挂在到新创建的Task中,并且是顶部
            newParent.addChild(mStartActivity, POSITION_TOP);
        } else {
            mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
        }
    }

这里的逻辑涉及到的 Task 就是上一步创建的 Task ,而 mStartActivity 则是“电话”在之前逻辑创建的 ActivityRecord 。

setNewTask的堆栈信息如下

在这里插入图片描述

另外这段逻辑里有个 ProtoLog 打印,日志如下:

在这里插入图片描述

3.4.2.1 小结

结合逻辑分析 + 堆栈信息 + ProtoLog ,可以确认 ActivityStarer::setNewTask 做的事情就是将 ActivityRecord 挂在到 Task 中,而且在顶部。

3.4.3 移动Task到容器顶部–moveToFront

这里提一下这个 moveToFront 方法,因为前面创建 Task 并添加到 DefaultTaskDisplayArea 时是往顶部添加,后面将 ActivityRecord 挂在到 Task 也是挂在到其顶部。所以这个函数其实没有什么实际操作。但是对于其他场景,这里也是一个重点方法。

调用链

Task::moveToFront
    Task::moveToFrontInner
        TaskDisplayArea::positionChildAt
            TaskDisplayArea::positionChildTaskAt
                ActivityTaskSupervisor::updateTopResumedActivityIfNeeded
                    ActivityRecord::onTopResumedActivityChanged      --触发TopResumedActivityChangeItem

主流程代码

首先确定一个问题:需要移动到顶部的是哪个 Task ? 这个 Task 所在的是在哪个哪个容器?
在 ActivityStarter::startActivityInner 的时候调用的是这段代码:

// targetTask 当前场景为null
mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);

已知 mTargetRootTask 是新创建给“电话”用的 Task, 而 mTargetRootTask.getRootTask() 返回值当前场景是 mTargetRootTask 本身。

# Task

    void moveToFront(String reason, Task task) {
        if (!isAttached()) {
            return;
        }
        mTransitionController.recordTaskOrder(this);

        final TaskDisplayArea taskDisplayArea = getDisplayArea();

        if (!isActivityTypeHome() && returnsToHomeRootTask()) {
            // Make sure the root home task is behind this root task since that is where we
            // should return to when this root task is no longer visible.
            taskDisplayArea.moveHomeRootTaskToFront(reason + " returnToHome");
        }

        final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedRootTask() : null;
        if (task == null) {
            // 当前场景为null,所以会赋值成新建的 Task ,也就是 mTargetRootTask 也就是电话的 Task
            task = this;
        }
        // 这里调用 getParent 就是 DefaultTaskDisplayArea 了。
        // 把当前的 Task 移动到 DefaultTaskDisplayArea 的最前面
        task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
        taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
    }

再回答一下问题:移动的是新建的电话 Task ,它的父容器是 DefaultTaskDisplayArea ,所以是把这个 Task 移动到 DefaultTaskDisplayArea 的最前面。

mTargetRootTask.getRootTask 返回的是顶部的 Task, 当前 Task 上一层是 TaskDisplayArea 类型 (name为DefaultTaskDisplayArea)

而 mTargetRootTask.getParent 返回的父容器(不限制是 Task),则是 name 为 DefaultTaskDisplayArea 的 TaskDisplayArea。

getRootTask 和 getParent 的区别可以自行去源码中看看

到这里,AMS已经将需要的 ActivityRecord 和 Task 创建并且挂载到层级树中,接下来将是需要处理 TargetActivity 启动和显示逻辑了

3.4.4 显示Activity–resumeFocusedTasksTopActivities

首先看方法名目的需要显示一个栈顶的 Activity ,那说的不就是 TargetActivity 嘛,TargetActivity 的 ActivityRecord 已经创建并且移动到栈顶了。
前面该做的也都做好了,现在也确实是时候处理显示逻辑,这部分流程由 RootWindowContainer::resumeFocusedTasksTopActivities 执行,调用链如下:

RootWindowContainer::resumeFocusedTasksTopActivities
    Task::resumeTopActivityUncheckedLocked
        Task::resumeTopActivityInnerLocked
            TaskFragment::resumeTopActivity 
                TaskDisplayArea::pauseBackTasks  --   pause LauncherActivity 
                    WindowContainer::forAllLeafTask
                        TaskFragment::forAllLeafTaskFragments
                            TaskFragment::startPausing
                                TaskFragment::startPausing
                                    TaskFragment::schedulePauseActivity --构建 PauseActivityItem,这里是触发暂停launch
                ActivityTaskManagerService::startProcessAsync     -- 创建“电话”进程

重点方法在 TaskFragment::resumeTopActivity 开始处理,先简单看一下前面的调用是怎么样的。

# RootWindowContainer
    // 第四个参数为 false
    boolean resumeFocusedTasksTopActivities(
            Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,
            boolean deferPause) {
                ......
                if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()
                        || getTopDisplayFocusedRootTask() == targetRootTask)) {
                    
                    result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,
                            deferPause);
                }
                ......
            }
# Task
    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options,
            boolean deferPause) {
                ......
                if (isLeafTask()) {
                    if (isFocusableAndVisible()) {
                        someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);
                    }
                }
                ......
            }

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,
            boolean deferPause) {
                ......
                // deferPause = false
                resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
                ......
            }

由于篇幅原因,下面的 TaskFragment::resumeTopActivity 流程放在下一篇。

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

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

相关文章

MySQL:存储引擎简介和库的基本操作

目录 一、存储引擎 1、什么是存储引擎&#xff1f; 2、存储引擎的分类 关系型数据库存储引擎&#xff1a; 非关系型数据库存储引擎&#xff1a; 分布式数据库存储引擎&#xff1a; 3、常用的存储引擎及优缺点 1、InnoDb存储引擎 2、MyISAM存储引擎 3、MEMORY存储引擎 …

android kotlin Extension扩展函数

1、新建一个kt文件&#xff1a; 2、代码&#xff1a; class User(var name:String)/**扩展函数**/ fun User.Print(){print("用户名 $name") }// 扩展函数 swap,调换不同位置的值 fun MutableList<Int>.swap(index1: Int, index2: Int) {val tmp this[index1…

组合逻辑元件与时序逻辑元件

组合逻辑元件和时序逻辑元件都是数字电路中的基本构建块&#xff0c;但它们在功能和结构上存在显著差异。 1. 组合逻辑元件: 内容: 组合逻辑元件的输出仅取决于当前的输入&#xff0c;而与之前的输入无关。 它们没有记忆功能。 常见的组合逻辑元件包括&#xff1a; 与门 (AND…

Java | Leetcode Java题解之第437题路径总和III

题目&#xff1a; 题解&#xff1a; class Solution {public int pathSum(TreeNode root, int targetSum) {Map<Long, Integer> prefix new HashMap<Long, Integer>();prefix.put(0L, 1);return dfs(root, prefix, 0, targetSum);}public int dfs(TreeNode root,…

红帽RHCE和RHCA有什么区别

在红帽认证体系当中&#xff0c;RHCE&#xff08;Red Hat Certified Engineer&#xff09;以及 RHCA&#xff08;Red Hat Certified Architect&#xff09;乃是两项极具声望的高级认证。众多人士对于它们彼此之间存在的区别&#xff0c;深感困惑不解。接下来&#xff0c;IT 小编…

UE4_Niagara基础实例—3、使用自定义模块二

效果&#xff1a; 粒子限制在球形范围内 操作步骤&#xff1a; 1、因为我们要对粒子的位置进行控制&#xff0c;所以需要开启本地空间&#xff0c;粒子与发射器位置有关。使用GPU计算模拟&#xff0c;需勾选固定边界。 2、简单调节其它参数&#xff1a; 3、现在我想把粒子锁定…

【Linux的内存管理】

为什么需要内存管理 分段和分页内存分段内存分页 分页情况下&#xff0c;虚拟内存如何映射到物理地址页表原理多级页表 TLB快表段页式内存管理需要为什么进程地址空间Linux的进程虚拟地址空间管理进程地址空间如何分配虚拟内存虚拟内存的管理程序编译后的二进制文件如何映射到虚…

《Zeotero的学习》

学习视频链接 Zeotera的安装 官网点击download&#xff0c;选择合适的版本进行下载&#xff0c;并安装插件。 下载完成之后&#xff0c;点击安装包&#xff0c;一路默认就可以。如果不想下载在C盘&#xff0c;可以在步骤中选择自定义路径。 Zeotero的注册 官网进行注册&am…

AIGC: 从两个维度快速选择大模型开发技术路线

在当今人工智能飞速发展的时代&#xff0c;大模型开发技术路线的选择至关重要。本文将从两个维度出发&#xff0c;为大家快速介绍不同的大模型开发技术路线&#xff0c;帮助你在开发过程中做出明智的决策。 一、两个维度解析 传入大模型的信息 低要求&#xff1a;传入的信息相…

【D3.js in Action 3 精译_025】3.4 让 D3 数据适应屏幕(中)—— 线性比例尺的用法

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

Activity的生命周期分析

目录 Activity的生命周期全面分析 典型情况下的生命周期分析 异常情况下的生命周期分析 情况1&#xff1a;资源相关的系统配置发生改变导致Activity被杀死并重新创建 Activity的生命周期全面分析 在Android开发中&#xff0c;Activity的生命周期是非常重要的概念。它描述了…

数仓建模:DataX同步Mysql数据到Hive如何批量生成建表语句?| 基于SQL实现

目录 一、需求 二、实现步骤 1.数据类型转换维表 2.sql批量生成建表语句 三、小结 如果觉得本文对你有帮助&#xff0c;那么不妨也可以选择去看看我的博客专栏 &#xff0c;部分内容如下&#xff1a; 数字化建设通关指南 专栏 原价99&#xff0c;现在活动价39.9&#x…

前端使用xlsx-js-style导出Excel,带样式,并处理合并单元格边框显示不全和动态插入表头解决

一、在学习之前&#xff0c;先给出一些学习/下载地址&#xff1a; xlsx-js-style下载地址 https://github.com/gitbrent/xlsx-js-style 或者 https://www.npmjs.com/package/xlsx-js-style SheetJS中文教程&#xff1a; https://xlsx.nodejs.cn/docs/csf/cell 二、先看样…

双指针---(部分地更新)

双指针 复写零 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不要从函数返回任何东西。 …

Tableau 瀑布图应用示例

通过探索 10 个示例&#xff0c;将瀑布图的应用拓展到更深层次的业务分析&#xff01; 作为一种直观展示数据变化的图表&#xff0c;瀑布图被广泛应用在业务分析中。同时&#xff0c;借助 Tableau 2024.2 中的 Viz Extensions&#xff0c;如今我们可以快速在 Tableau 中实现瀑布…

Vue3-TS-Lodash:理解Lodash / 常用方法积累

一、Lodash官网 Lodash 简介 | Lodash中文文档 | Lodash中文网 二、理解Lodash Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。它提供了大量的函数来帮助你处理数组、数值、对象、字符串等&#xff0c;使你的代码更加简洁、易读和高效。Lodash 的设计哲学是…

25基于python的文本冒险岛游戏(源码+游戏简介+python代码学习攻略)校园招聘面试

基于python的文本冒险岛游戏&#xff08;源代码游戏简介python代码学习&#xff09;资源-CSDN文库https://download.csdn.net/download/m0_72216164/89817518 开头附上工作招聘面试必备问题噢~~包括综合面试题、无领导小组面试题资源文件免费&#xff01;全文干货。 工作招聘无…

HarmoneyOS--Ability(能力)、窗口、通知

标题 文章目录 一、什么是Ability?二、使用步骤(单例和多例)三、窗口四、通知 一、什么是Ability? 开发模式提供的开发功能抽象的描述。 其中重要的是UiAbility,界面组件能力,负责所有界面的处理。 通过配置可以变更单例,多例,指定实例,在module.json5中进行配置 如: 单例:l…

FreeRTOS的中断管理

前言 FreeRTOS的任务有优先级&#xff0c;MCU的硬件中断有中断优先级&#xff0c;这是两个不同的概念&#xff0c;FreeRTOS的任务管理要用到硬件中断&#xff0c;使用FreeRTOS时候也可以使用硬件中断&#xff0c;但是硬件中断ISR的设计要注意一些设计原则&#xff0c;在本节中我…

RVC变声器入门

主要参考资料&#xff1a; RVC变声器官方教程&#xff1a;10分钟克隆你的声音&#xff01;一键训练&#xff0c;低配显卡用户福音&#xff01;: https://www.bilibili.com/video/BV1pm4y1z7Gm/?spm_id_from333.337.search-card.all.click&vd_sourcedd284033cd0c4d1f3f59a2…