Android14 Activity 启动过程详解 3 —— SystemServer 处理请求之 Task 的构建

本文基于 android-14.0.0_r15 版本讲解:

1. 整体流程

接上一节,我们接着看 startActivityUnchecked 方法的实现:

java 复制代码
    // frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, @BalCode int balCode,
            NeededUriGrants intentGrants, int realCallingUid) {
        
        // ......
        
        try {
            mService.deferWindowLayout();
            transitionController.collect(r);
            try {
                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");

                // 关注点
                result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                        startFlags, options, inTask, inTaskFragment, balCode,
                        intentGrants, realCallingUid);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                startedActivityRootTask = handleStartResult(r, options, result, newTransition,
                        remoteTransition);
            }
        } finally {
            mService.continueWindowLayout();
        }
        postStartActivityProcessing(r, result, startedActivityRootTask);

        return result;
    }

接着调用到 startActivityInner 方法:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

   /**
     * Start an activity and determine if the activity should be adding to the top of an existing
     * task or delivered new intent to an existing activity. Also manipulating the activity task
     * onto requested or valid root-task/display.
     *
     * Note: This method should only be called from {@link #startActivityUnchecked}.
     */
    // TODO(b/152429287): Make it easier to exercise code paths through startActivityInner
    @VisibleForTesting
    int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, @BalCode int balCode,
            NeededUriGrants intentGrants, int realCallingUid) {
        
        // 初始化各类参数
        setInitialState(r, options, inTask, inTaskFragment, startFlags, sourceRecord,
                voiceSession, voiceInteractor, balCode, realCallingUid);

        // 根据 launchMode 和 Intent 中的 FLAG_ACTIVITY_NEW_TASK 等 flag 综合计算 activity 的启动模式,
	    // 结果保存在 mLaunchFlags 中。计算的过程不仅要考虑目标 activity 的 launchMode,
	    // 也要考虑原来 activity 的 launchMode 和 Intent 中所带着的 flag
        computeLaunchingTaskFlags();
        
        mIntent.setFlags(mLaunchFlags); //270532608

        boolean dreamStopping = false;

        // ......

    
        // launcher 中有两个 Task
        // launcher 中的根 Task
        final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
        // launcher 中的叶子 Task
        final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
        // null,没有可以复用的 Task
        final Task reusedTask = getReusableTask();

        // ......

        // null,computeTargetTask 查找是否已经存在目标 Task,当前场景下没有,返回 null
        final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
        // true
        final boolean newTask = targetTask == null;
        mTargetTask = targetTask; // null

        // 计算启动参数,结果保存在 mLaunchParams 中 
        computeLaunchParams(r, sourceRecord, targetTask);

        // Check if starting activity on given task or on a new task is allowed.
        int startResult = isAllowedToStart(r, newTask, targetTask); // 0
        
        // ......

        final ActivityRecord targetTaskTop = newTask
                ? null : targetTask.getTopNonFinishingActivity(); // null
        if (targetTaskTop != null) { // 不进入
            // ......
        } else { //进入
            mAddingToTask = true;
        }

        // 启动当前已经在栈顶的 Activity 情况的处理
        // If the activity being launched is the same as the one currently at the top, then
        // we need to check if it should only be launched once.
        final Task topRootTask = mPreferredTaskDisplayArea.getFocusedRootTask(); 
        if (topRootTask != null) { // 进入
            startResult = deliverToCurrentTopIfNeeded(topRootTask, intentGrants); // 0
            if (startResult != START_SUCCESS) { //不进入 
                return startResult;
            }
        }

        if (mTargetRootTask == null) { // 进入
            // 关注点 1
            // 创建一个 Task 对象,也就是目标 Task
            mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
                    mOptions);
        }
        if (newTask) { // 进入
            final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                    ? mSourceRecord.getTask() : null; // null
            // 关注点2
            setNewTask(taskToAffiliate); // 把目标 ActivityRecord 插入新创建的 Task
        } else if (mAddingToTask) {
            addOrReparentStartingActivity(targetTask, "adding to task");
        }

        if (!mAvoidMoveToFront && mDoResume) { // 进入

            // 关注点 3
            // 移动新创建的 Task 到 TaskDisplayArea 的子节点的栈顶
            mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
            if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming()
                    && !dreamStopping) { // 不进入
                // Launching underneath dream activity (fullscreen, always-on-top). Run the launch-
                // -behind transition so the Activity gets created and starts in visible state.
                mLaunchTaskBehind = true;
                r.mLaunchTaskBehind = true;
            }
        }

        // 给目标 Activity 添加 Uri 权限
        mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
                mStartActivity.getUriPermissionsLocked());
        // ......\
        final Task startedTask = mStartActivity.getTask();
        if (newTask) {
            EventLogTags.writeWmCreateTask(mStartActivity.mUserId, startedTask.mTaskId,
                    startedTask.getRootTaskId(), startedTask.getDisplayId());
        }
        mStartActivity.logStartActivity(EventLogTags.WM_CREATE_ACTIVITY, startedTask);

        mStartActivity.getTaskFragment().clearLastPausedActivity();


        // 电源管理,了解 
        mRootWindowContainer.startPowerModeLaunchIfNeeded(
                false /* forceSend */, mStartActivity);

        final boolean isTaskSwitch = startedTask != prevTopTask;
        // 关注点 4
        // 调整目标 Task 相关参数,开启转场动画,为启动 Activity 做好准备
        mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
                mOptions, sourceRecord);
        if (mDoResume) { // 进入
            final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
            if (!mTargetRootTask.isTopActivityFocusable()
                    || (topTaskActivity != null && topTaskActivity.isTaskOverlay()
                    && mStartActivity != topTaskActivity)) { // 不进入
                // ......
            } else {
                
                // ......

                // 关注点 5
                // 生命周期处理
                mRootWindowContainer.resumeFocusedTasksTopActivities(
                        mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
            }
        }
        mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);

        // Update the recent tasks list immediately when the activity starts
        mSupervisor.mRecentTasks.add(startedTask);
        mSupervisor.handleNonResizableTaskIfNeeded(startedTask,
                mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);

        // If Activity's launching into PiP, move the mStartActivity immediately to pinned mode.
        // Note that mStartActivity and source should be in the same Task at this point.
        if (mOptions != null && mOptions.isLaunchIntoPip()
                && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()
                && balCode != BAL_BLOCK) {
            mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
                    sourceRecord, "launch-into-pip");
        }

        return START_SUCCESS;
    }
  • setInitialState 方法用于初始化一堆参数
  • computeLaunchingTaskFlags 方法,根据 launchMode 和 Intent 中的 FLAG_ACTIVITY_NEW_TASK 等 flag 综合计算 activity 的启动模式,结果保存在 mLaunchFlags 中。计算的过程不仅要考虑目标 activity 的 launchMode,也要考虑原来 activity 的 launchMode 和 Intent 中所带着的 flag
  • getOrCreateRootTask 这个是我们的关注点 1,后面会详细分析其实现,用于创建一个 Task 对象,也就是目标 Task。
  • setNewTask(taskToAffiliate) 是关注点 2,其主要作用是把目标 ActivityRecord 插入新创建的 Task
  • 关注点 3 mTargetRootTask.getRootTask().moveToFront 移动新创建的 Task 到 TaskDisplayArea 的子节点的栈顶
  • 调用 grantUriPermissionUncheckedFromIntent 给目标 Activity 添加 uri 权限
  • 关注点 4 TargetRootTask.startActivityLocked:调整目标 Task 内部参数,开启转场动画,为启动 Activity 做好准备
  • 关注点 5 resumeFocusedTasksTopActivities :生命周期处理

2. 核心要点分析

在了解了总体流程以后,接下来我们就来分析重要的几个关注点:

关注点 1,getOrCreateRootTask 方法创建一个目标 Task 对象:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

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

计算出两个参数后,接着调用 getOrCreateRootTask 方法:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
    Task getOrCreateRootTask(@Nullable ActivityRecord r,
            @Nullable ActivityOptions options, @Nullable Task candidateTask,
            @Nullable Task sourceTask, boolean onTop,
            @Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {
        
        // ......

        TaskDisplayArea taskDisplayArea = null;
        if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) { // 进入
            taskDisplayArea = launchParams.mPreferredTaskDisplayArea; // 拿到 TaskDisplayArea 对象
        } else if (options != null) { // 不进入
           //......
        }

        final int activityType = resolveActivityType(r, options, candidateTask); // 1
        if (taskDisplayArea != null) {
            if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) { // 进入
                // 直接返回了
                return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
                        sourceTask, launchParams, launchFlags, activityType, onTop);
            } else {
                taskDisplayArea = null;
            }
        }

       // ......
    }

接着调用 taskDisplayArea.getOrCreateRootTask 方法:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java
    Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
            @Nullable Task candidateTask, @Nullable Task sourceTask,
            @Nullable LaunchParams launchParams, int launchFlags, int activityType, boolean onTop) {
        int windowingMode = WINDOWING_MODE_UNDEFINED;
        if (launchParams != null) {
            // If launchParams isn't null, windowing mode is already resolved.
            windowingMode = launchParams.mWindowingMode;
        } else if (options != null) {
            // If launchParams is null and options isn't let's use the windowing mode in the
            // options.
            windowingMode = options.getLaunchWindowingMode();
        }
        // Validate that our desired windowingMode will work under the current conditions.
        // UNDEFINED windowing mode is a valid result and means that the new root task will inherit
        // it's display's windowing mode.
        windowingMode = validateWindowingMode(windowingMode, r, candidateTask); // 0

        return getOrCreateRootTask(windowingMode, activityType, onTop, candidateTask, sourceTask,
                options, launchFlags);
    }

计算出 windowmode,当前情景下是 0(WINDOWING_MODE_UNDEFINED),接着调用另一个重载 getOrCreateRootTask:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java
    Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
            @Nullable Task candidateTask, @Nullable Task sourceTask,
            @Nullable ActivityOptions options, int launchFlags) {
        final int resolvedWindowingMode =
                windowingMode == WINDOWING_MODE_UNDEFINED ? getWindowingMode() : windowingMode; // 1
        // .......
        return new Task.Builder(mAtmService)
                .setWindowingMode(windowingMode)
                .setActivityType(activityType)
                .setOnTop(onTop)
                .setParent(this)
                .setSourceTask(sourceTask)
                .setActivityOptions(options)
                .setLaunchFlags(launchFlags)
                .build();
    }

最后,调用 Task.Builder 构建一个 Task 对象,需要注意的是,这里通过 setParent 将当前 TaskDisplayArea 对象设置为父节点。

目标 Task 就创建好了,并且插入了窗口容器树中,成为了 TaskDisplayArea 的子节点。

接着我们看关注点2 setNewTask(taskToAffiliate) 该方法的主要作用是把目标 ActivityRecord 插入新创建的 Task 中。

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
    private void setNewTask(Task taskToAffiliate) {
        final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
        // reuseOrCreateTask 实际就是返回之前新创建的 Task 对象
        final Task task = mTargetRootTask.reuseOrCreateTask(
                mStartActivity.info, mIntent, mVoiceSession,
                mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
        task.mTransitionController.collectExistenceChange(task);

        // addOrReparentStartingActivity 方法将目标 ActivityRecord 插入新创建的 Task 中
        addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");

        ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",
                mStartActivity, mStartActivity.getTask());

        if (taskToAffiliate != null) {
            mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
        }
    }

接着调用 addOrReparentStartingActivity 方法:

java 复制代码
    private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
        TaskFragment newParent = task; // 刚刚新创建的 Task
        if (mInTaskFragment != null) { // 不进入
           // ......
        } else {
            TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null; // null
            if (candidateTf == null) { // 进入
                final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */); // null
                if (top != null) {
                    candidateTf = top.getTaskFragment();
                }
            }
            if (candidateTf != null && candidateTf.isEmbedded()
                    && canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) { // 不进入
                // Use the embedded TaskFragment of the top activity as the new parent if the
                // activity can be embedded.
                newParent = candidateTf;
            }
        }
        if (mStartActivity.getTaskFragment() == null
                || mStartActivity.getTaskFragment() == newParent) {
            // 实际就是执行 TaskFragment 的 addChild,TaskFragment 就是刚刚创建的 Task
            newParent.addChild(mStartActivity, POSITION_TOP);
        } else {
            mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
        }
    }

我们接着看 addChild:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java 

    // child 就是之前创建的目标 ActivityRecord
    // index:POSITION_TOP
    @Override
    void addChild(WindowContainer child, int index) {
        ActivityRecord r = topRunningActivity(); //null
        mClearedTaskForReuse = false;
        mClearedTaskFragmentForPip = false;
        mClearedForReorderActivityToFront = false;

        final ActivityRecord addingActivity = child.asActivityRecord();
        final boolean isAddingActivity = addingActivity != null;
        final Task task = isAddingActivity ? getTask() : null;

        // If this task had any activity before we added this one.
        boolean taskHadActivity = task != null && task.getTopMostActivity() != null;
        // getActivityType() looks at the top child, so we need to read the type before adding
        // a new child in case the new child is on top and UNDEFINED.
        final int activityType = task != null ? task.getActivityType() : ACTIVITY_TYPE_UNDEFINED;

        // 调用父类的 addChild
        super.addChild(child, index);

        if (isAddingActivity && task != null) {
            // TODO(b/207481538): temporary per-activity screenshoting
            if (r != null && BackNavigationController.isScreenshotEnabled()) {
                ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
                        r.mActivityComponent.flattenToString());
                Rect outBounds = r.getBounds();
                ScreenCapture.ScreenshotHardwareBuffer backBuffer = ScreenCapture.captureLayers(
                        r.mSurfaceControl,
                        new Rect(0, 0, outBounds.width(), outBounds.height()),
                        1f);
                mBackScreenshots.put(r.mActivityComponent.flattenToString(), backBuffer);
            }
            addingActivity.inHistory = true;
            task.onDescendantActivityAdded(taskHadActivity, activityType, addingActivity);
        }

        final WindowProcessController hostProcess = getOrganizerProcessIfDifferent(addingActivity);
        if (hostProcess != null) {
            hostProcess.addEmbeddedActivity(addingActivity);
        }
    }

接着调用父类的 addChild 方法:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    void addChild(E child, int index) {
        if (!child.mReparenting && child.getParent() != null) { // 不进入
            throw new IllegalArgumentException("addChild: container=" + child.getName()
                    + " is already a child of container=" + child.getParent().getName()
                    + " can't add to container=" + getName()
                    + "\n callers=" + Debug.getCallers(15, "\n"));
        }

        if ((index < 0 && index != POSITION_BOTTOM)
                || (index > mChildren.size() && index != POSITION_TOP)) { // 不进入
            throw new IllegalArgumentException("addChild: invalid position=" + index
                    + ", children number=" + mChildren.size());
        }

        if (index == POSITION_TOP) {
            index = mChildren.size();
        } else if (index == POSITION_BOTTOM) {
            index = 0;
        }

        mChildren.add(index, child); // 添加到 Task 的子节点

        // Set the parent after we've actually added a child in case a subclass depends on this.
        child.setParent(this);
    }

核心的流程就是将 ActivityRecord 添加到 Task 的子节点 mChildren 中。

关注点 3 mTargetRootTask.getRootTask().moveToFront 移动新创建的 Task 到 TaskDisplayArea 的子节点的栈顶

java 复制代码
    // frameworks/base/services/core/java/com/android/server/wm/Task.java
    void moveToFront(String reason, Task task) {
        if (!isAttached()) {
            return;
        }
        mTransitionController.recordTaskOrder(this);

        // 获取到 TaskDisplayArea 对象
        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");
        }

        // 就是新创建的 Task
        final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedRootTask() : null;
        if (task == null) { // 进入,this 也是新创建的 Task
            task = this;
        }

        // 将新创建的 Task 放到 TaskDisplayArea 的子节点的栈顶
        task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
        // 更新 mLastFocusedRootTask 的值
        taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
    }

关注点 4 TargetRootTask.startActivityLocked:调整 Task 相关参数,开启转场动画,为启动 Activity 做好准备:

java 复制代码
// frameworks/base/services/core/java/com/android/server/wm/Task.java

// r:目标 ActivityRecord 对象
    void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
            boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
        
        // null
        final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
        // 新创建的 Task 对象
        Task rTask = r.getTask();
        // true
        final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
        // false
        final boolean isOrhasTask = rTask == this || hasChild(rTask);
        // mLaunchTaskBehind tasks get placed at the back of the task stack.
        if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) { // 进入
            // Last activity in task had been removed or ActivityManagerService is reusing task.
            // Insert or replace.
            // Might not even be in.
            // 把 Task 放到父节点的栈顶,感觉这里有点重复了
            positionChildAtTop(rTask);
        }
        Task task = null;
        if (!newTask && isOrhasTask && !r.shouldBeVisible()) { // 不进入
            ActivityOptions.abort(options);
            return;
        }

        // Place a new activity at top of root task, so it is next to interact with the user.

        // If we are not placing the new activity frontmost, we do not want to deliver the
        // onUserLeaving callback to the actual frontmost activity
        final Task activityTask = r.getTask();

        // ......

        task = activityTask;

        // Slot the activity into the history root task and proceed
        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
                        + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());

        // ......

        final DisplayContent dc = mDisplayContent;
        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                "Prepare open transition: starting " + r);

        if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { // 不进入
          // ......
        } else { // 进入
            dc.prepareAppTransition(TRANSIT_OPEN); // 准备过渡动画
            mTaskSupervisor.mNoAnimActivities.remove(r);
        }
        if (newTask && !r.mLaunchTaskBehind) { // 进入
            // If a new task is being launched, then mark the existing top activity as
            // supporting picture-in-picture while pausing only if the starting activity
            // would not be considered an overlay on top of the current activity
            // (eg. not fullscreen, or the assistant)
            enableEnterPipOnTaskSwitch(pipCandidate,
                    null /* toFrontTask */, r, options); // 多窗口相关的设置
        }
        boolean doShow = true;

        if (newTask) { // 进入
            // Even though this activity is starting fresh, we still need
            // to reset it to make sure we apply affinities to move any
            // existing activities from other tasks in to it.
            // If the caller has requested that the target task be
            // reset, then do so.
            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { // 进入
                resetTaskIfNeeded(r, r);
                doShow = topRunningNonDelayedActivityLocked(null) == r; // true
            }
        } else if (options != null && options.getAnimationType()
                == ActivityOptions.ANIM_SCENE_TRANSITION) {
            doShow = false;
        }

        // ......

        if (r.mLaunchTaskBehind) { // 不进入
           // ......
        } else if (SHOW_APP_STARTING_PREVIEW && doShow) { // 进入
            // Figure out if we are transitioning from another activity that is
            // "has the same starting icon" as the next one.  This allows the
            // window manager to keep the previous window it had previously
            // created, if it still had one.
            Task baseTask = r.getTask();
            final ActivityRecord prev = baseTask.getActivity(
                    a -> a.mStartingData != null && a.showToCurrentUser());
            // 开启转场动画
            mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask,
                    isTaskSwitch, sourceRecord);
        }
    }

总结

代码非常繁琐,总结一下,核心的要点其实不多:

  • 创建好目标 Task 对象
  • 将 Task 对象放到正确的位置
  • 开启转场动画
  • Pause 前一个 Activity

参考资料

相关推荐
DS小龙哥几秒前
QT For Android开发-打开PPT文件
android·qt·powerpoint
试行1 小时前
Android实现自定义下拉列表绑定数据
android·java
Dingdangr6 小时前
Android中的Intent的作用
android
技术无疆6 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
GEEKVIP6 小时前
Android 恢复挑战和解决方案:如何从 Android 设备恢复删除的文件
android·笔记·安全·macos·智能手机·电脑·笔记本电脑
Jouzzy13 小时前
【Android安全】Ubuntu 16.04安装GDB和GEF
android·ubuntu·gdb
极客先躯13 小时前
java和kotlin 可以同时运行吗
android·java·开发语言·kotlin·同时运行
Good_tea_h16 小时前
Android中的单例模式
android·单例模式
计算机源码社20 小时前
分享一个基于微信小程序的居家养老服务小程序 养老服务预约安卓app uniapp(源码、调试、LW、开题、PPT)
android·微信小程序·uni-app·毕业设计项目·毕业设计源码·计算机课程设计·计算机毕业设计开题
丶白泽21 小时前
重修设计模式-结构型-门面模式
android