Analyze the Activity startup process

preface

I haven’t posted my blog for almost two years. During this period, I have been engaged in the development of Android system, so I don’t have much time to write articles. At the same time, because of my lack of knowledge accumulation, I found that my knowledge was too poor after going into the bottom development, so I have been settling myself. Two years to learn a lot of the knowledge of the system framework layer, horizontal knowledge or longitudinal depth has improved, recently will take time I know during the period of the knowledge and the module with source code analysis again, on the one hand, through the analysis of their own impression, but also let the students little detours, source code analysis process is very boring, but it is also the embodiment of the basic skills, I will also try to write easy to understand some, at the same time if there are mistakes in the article, or not easy to understand the place, but also hope to help correct, we learn together, together raise salary.

Introduction to the

In the process of App development, we start an Activity operation, it is normal, but most of the time, we have not thought about the process behind, this chapter I will combine the source code, analyze the process, take a look, the system in the end for us to do what the source code in the article because of length reasons, may be omitted, The full source code can be viewed at androidxref.com/, where I’m using pie-9.0.0_R3

Began to call

The entry function ContextWrapper startActivity

To start an Activity, we first call the Activity’s startActivity() method and click in to see what’s actually called ContextWrapper’s startActivity() method. Look at this method:

    @Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }
Copy the code

Instead of implementing it, we call the startActivity method of mBase, which is actually a ContextImpl instance passed in

ContextImpl.startActivity

// /frameworks/base/core/java/android/app/ContextImpl.java @Override public void startActivity(Intent intent) { warnIfCallingFromSystemProcess(); startActivity(intent, null); } /** @hide */ @Override public void startActivityAsUser(Intent intent, UserHandle user) { startActivityAsUser(intent, null, user); } @Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We // maintain this for backwards compatibility. final int targetSdkVersion = getApplicationInfo().targetSdkVersion; if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires  the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }Copy the code

The execStartActivity method is used to create the ActivityThread instance object in the Instrumentation method. Find the Java file and take a look at the implementation

Instrumentation.execStartActivity

// /frameworks/base/core/java/android/app/Instrumentation.java public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; Uri referrer = target ! = null ? target.onProvideReferrer() : null; if (referrer ! = null) { intent.putExtra(Intent.EXTRA_REFERRER, referrer); } if (mActivityMonitors ! = null) { synchronized (mSync) { final int N = mActivityMonitors.size(); for (int i=0; i<N; i++) { final ActivityMonitor am = mActivityMonitors.get(i); ActivityResult result = null; if (am.ignoreMatchingSpecificIntents()) { result = am.onStartActivity(intent); } if (result ! = null) { am.mHits++; return result; } else if (am.match(who, null, intent)) { am.mHits++; if (am.isBlocking()) { return requestCode >= 0 ? am.getResult() : null; } break; } } } } try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); int result = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! = null ? target.mEmbeddedID : null, requestCode, 0, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } return null; }Copy the code

You can see that the startActivity method of ActivityManagerService is actually called through ActivityManager

ActivityManagerService.startActivity

Other features of ActivityManagerService will not be covered here, but will be described in a later article

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

    @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
                true /*validateIncomingUser*/);
    }

    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {
        enforceNotIsolatedCaller("startActivity");

        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // TODO: Switch to user app stacks here.
        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setMayWait(userId)
                .execute();

    }
Copy the code

See what have done here, the first is testing program is enforced, will throw a SecurityException then is to get to the caller’s user id is the last call ActivityStartController. ObtainStarter, set all the launch parameters, Returns an ActivityStarter object and executes the execute method

ActivityStarter.execute

// /frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java int execute() { try { // TODO(b/64750076): Look into passing request directly to these methods to allow // for transactional diffs and preprocessing. if (mRequest.mayWait) { return startActivityMayWait(mRequest.caller, mRequest.callingUid, mRequest.callingPackage, mRequest.intent, mRequest.resolvedType, mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.startFlags, mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig, mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId, mRequest.inTask, mRequest.reason, mRequest.allowPendingRemoteAnimationRegistryLookup); } else { return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo, mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo, mRequest.resultWho, mRequest.requestCode, mRequest.callingPid, mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid, mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.componentSpecified, mRequest.outActivity, mRequest.inTask, mRequest.reason, mRequest.allowPendingRemoteAnimationRegistryLookup); } } finally { onExecutionComplete(); }}Copy the code

There are two methods, and since the setMayWait(userId) method was called above, we’ll go to startActivityMayWait and look at the implementation of that method

ActivityStarter.startActivityMayWait

    // /frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
    private int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
            int userId, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup) {
        // Refuse possible leaked file descriptors
        if (intent != null && intent.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }
        mSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
        //查找PMS中的注册表
        boolean componentSpecified = intent.getComponent() != null;
        //得到调用的Pid和Uid
        final int realCallingPid = Binder.getCallingPid();
        final int realCallingUid = Binder.getCallingUid();

        int callingPid;
        if (callingUid >= 0) {
            callingPid = -1;
        } else if (caller == null) {
            callingPid = realCallingPid;
            callingUid = realCallingUid;
        } else {
            callingPid = callingUid = -1;
        }

        // Save a copy in case ephemeral needs it
        //拷贝两个Intent,一个临时使用,一个防止修改后无法复原
        final Intent ephemeralIntent = new Intent(intent);
        // Don't modify the client's object!
        intent = new Intent(intent);
        //当有对应的组件,并且传递的数据不为null,并且action指定了Intent.ACTION_VIEW,同时满足是安装程序对应的Activity
        if (componentSpecified
                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
                && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
                && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
                && mService.getPackageManagerInternalLocked()
                        .isInstantAppInstallerComponent(intent.getComponent())) {
            // intercept intents targeted directly to the ephemeral installer the
            // ephemeral installer should never be started with a raw Intent; instead
            // adjust the intent so it looks like a "normal" instant app launch
            intent.setComponent(null /*component*/);
            componentSpecified = false;
        }

        //通过PMS解析满足Intent等参数要求的信息,内部包含了四大组件的清单文件中声明的信息
        ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                0 /* matchFlags */,
                        computeResolveFilterUid(
                                callingUid, realCallingUid, mRequest.filterCallingUid));
        //当rInfo为null的处理
        if (rInfo == null) {
            UserInfo userInfo = mSupervisor.getUserInfo(userId);
            if (userInfo != null && userInfo.isManagedProfile()) {
                // Special case for managed profiles, if attempting to launch non-cryto aware
                // app in a locked managed profile from an unlocked parent allow it to resolve
                // as user will be sent via confirm credentials to unlock the profile.
                UserManager userManager = UserManager.get(mService.mContext);
                boolean profileLockedAndParentUnlockingOrUnlocked = false;
                long token = Binder.clearCallingIdentity();
                try {
                    UserInfo parent = userManager.getProfileParent(userId);
                    profileLockedAndParentUnlockingOrUnlocked = (parent != null)
                            && userManager.isUserUnlockingOrUnlocked(parent.id)
                            && !userManager.isUserUnlockingOrUnlocked(userId);
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
                if (profileLockedAndParentUnlockingOrUnlocked) {
                    rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                            PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                            computeResolveFilterUid(
                                    callingUid, realCallingUid, mRequest.filterCallingUid));
                }
            }
        }
        // Collect information about the target of the Intent.
        //通过resolveActivity得到目标Activity的信息
        ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
        //获取到AMS锁时的操作
        synchronized (mService) {
            //得到当前栈
            final ActivityStack stack = mSupervisor.mFocusedStack;
            stack.mConfigWillChange = globalConfig != null
                    && mService.getGlobalConfiguration().diff(globalConfig) != 0;
            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                    "Starting activity when config will change = " + stack.mConfigWillChange);
            //清除Pid和Uid用于安全检查
            final long origId = Binder.clearCallingIdentity();
            //若ActivityInfo不为null,并且有PRIVATE_FLAG_CANT_SAVE_STATE标记,意味着调用则为heavy-weight process
            if (aInfo != null &&
                    (aInfo.applicationInfo.privateFlags
                            & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 &&
                    mService.mHasHeavyWeightFeature) {
                // This may be a heavy-weight process!  Check to see if we already
                // have another, different heavy-weight process running.
                //进程名为Application中的包名
                if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
                    //得到当前运行的重量级进程的记录
                    final ProcessRecord heavy = mService.mHeavyWeightProcess;
                    //当前运行的重量级进程与要启动的不符
                    if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid
                            || !heavy.processName.equals(aInfo.processName))) {
                        int appCallingUid = callingUid;
                        if (caller != null) {
                            //得到调用者进程记录
                            ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
                            if (callerApp != null) {
                                appCallingUid = callerApp.info.uid;
                            } else {
                                Slog.w(TAG, "Unable to find app for caller " + caller
                                        + " (pid=" + callingPid + ") when starting: "
                                        + intent.toString());
                                SafeActivityOptions.abort(options);
                                return ActivityManager.START_PERMISSION_DENIED;
                            }
                        }

                        IIntentSender target = mService.getIntentSenderLocked(
                                ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                                appCallingUid, userId, null, null, 0, new Intent[] { intent },
                                new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
                                        | PendingIntent.FLAG_ONE_SHOT, null);
                        //新的Intent
                        Intent newIntent = new Intent();
                        if (requestCode >= 0) {
                            // Caller is requesting a result.
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
                        }
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
                                new IntentSender(target));
                        //当当前重量级进程的ActivityRecord数大于0时
                        if (heavy.activities.size() > 0) {
                            //得到第一个Activity,并将名字和栈set进新的Intent中
                            ActivityRecord hist = heavy.activities.get(0);
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
                                    hist.packageName);
                            newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
                                    hist.getTask().taskId);
                        }
                        newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
                                aInfo.packageName);
                        newIntent.setFlags(intent.getFlags());
                        newIntent.setClassName("android",
                                HeavyWeightSwitcherActivity.class.getName());
                        intent = newIntent;
                        resolvedType = null;
                        caller = null;
                        callingUid = Binder.getCallingUid();
                        callingPid = Binder.getCallingPid();
                        componentSpecified = true;
                        rInfo = mSupervisor.resolveIntent(intent, null /*resolvedType*/, userId,
                                0 /* matchFlags */, computeResolveFilterUid(
                                        callingUid, realCallingUid, mRequest.filterCallingUid));
                        aInfo = rInfo != null ? rInfo.activityInfo : null;
                        if (aInfo != null) {
                            aInfo = mService.getActivityInfoForUser(aInfo, userId);
                        }
                    }
                }
            }
            //创建新的ActivityRecord
            final ActivityRecord[] outRecord = new ActivityRecord[1];
            //执行startActivity
            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
                    allowPendingRemoteAnimationRegistryLookup);

            Binder.restoreCallingIdentity(origId);

            if (stack.mConfigWillChange) {
                // 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.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                        "updateConfiguration()");
                stack.mConfigWillChange = false;
                if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                        "Updating to new configuration after starting activity.");
                mService.updateConfigurationLocked(globalConfig, null, false);
            }
            //当结果不为null
            if (outResult != null) {
                outResult.result = res;

                final ActivityRecord r = outRecord[0];
                判断启动状态
                switch(res) {
                    case START_SUCCESS: {
                        //目标Activiy要运行在一个新的应用进程中,因此需要等待应用程序正常启动,并处理相关请求
                        mSupervisor.mWaitingActivityLaunched.add(outResult);
                        do {
                            try {
                                //一直等待,直到outResult显示Activity对应的Task成为Front task
                                mService.wait();
                            } catch (InterruptedException e) {
                            }
                        } while (outResult.result != START_TASK_TO_FRONT
                                && !outResult.timeout && outResult.who == null);
                        if (outResult.result == START_TASK_TO_FRONT) {
                            res = START_TASK_TO_FRONT;
                        }
                        break;
                    }
                    case START_DELIVERED_TO_TOP: {
                        outResult.timeout = false;
                        outResult.who = r.realActivity;
                        outResult.totalTime = 0;
                        outResult.thisTime = 0;
                        break;
                    }
                    case START_TASK_TO_FRONT: {
                        // ActivityRecord may represent a different activity, but it should not be
                        // in the resumed state.
                        if (r.nowVisible && r.isState(RESUMED)) {
                            outResult.timeout = false;
                            outResult.who = r.realActivity;
                            outResult.totalTime = 0;
                            outResult.thisTime = 0;
                        } else {
                            outResult.thisTime = SystemClock.uptimeMillis();
                            //Activity对应的task拉到后台后,一直要等待该界面被加载
                            mSupervisor.waitActivityVisible(r.realActivity, outResult);
                            // Note: the timeout variable is not currently not ever set.
                            do {
                                try {
                                    mService.wait();
                                } catch (InterruptedException e) {
                                }
                            } while (!outResult.timeout && outResult.who == null);
                        }
                        break;
                    }
                }
            }

            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]);
            return res;
        }
    }
Copy the code

There is a lot of code in this, in order to understand, I add the comments in the code inside the summary: Parse the ActivityInfo corresponding to the Intent, get the foreground Task that started the Activity, start the Activity with startActivity, process the result, and then move on, call the startActivity method, This method calls the more important method startActivityUnchecked, check the source code

Start mode and other related processing

ActivityStarter.startActivityUnchecked

    // /frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java
    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {

        //初始化环境和LauncherModeFlags
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);
        //计算关于task的启动参数
        computeLaunchingTaskFlags();
        //计算SourceStack,也就是调用者所在的Stack
        computeSourceStack();

        mIntent.setFlags(mLaunchFlags);

        //复用ActivityRecord
        ActivityRecord reusedActivity = getReusableIntentActivity();

        int preferredWindowingMode = WINDOWING_MODE_UNDEFINED;
        int preferredLaunchDisplayId = DEFAULT_DISPLAY;
        if (mOptions != null) {
            preferredWindowingMode = mOptions.getLaunchWindowingMode();
            preferredLaunchDisplayId = mOptions.getLaunchDisplayId();
        }

        // windowing mode and preferred launch display values from {@link LaunchParams} take
        // priority over those specified in {@link ActivityOptions}.
        if (!mLaunchParams.isEmpty()) {
            if (mLaunchParams.hasPreferredDisplay()) {
                preferredLaunchDisplayId = mLaunchParams.mPreferredDisplayId;
            }

            if (mLaunchParams.hasWindowingMode()) {
                preferredWindowingMode = mLaunchParams.mWindowingMode;
            }
        }

        if (reusedActivity != null) {
            // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
            // still needs to be a lock task mode violation since the task gets cleared out and
            // the device would otherwise leave the locked task.
            if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
                    (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                            == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
                return START_RETURN_LOCK_TASK_MODE_VIOLATION;
            }

            // True if we are clearing top and resetting of a standard (default) launch mode
            // ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
            final boolean clearTopAndResetStandardLaunchMode =
                    (mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
                            == (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                    && mLaunchMode == LAUNCH_MULTIPLE;

            // If mStartActivity does not have a task associated with it, associate it with the
            // reused activity's task. Do not do so if we're clearing top and resetting for a
            // standard launchMode activity.
            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
                mStartActivity.setTask(reusedActivity.getTask());
            }

            if (reusedActivity.getTask().intent == null) {
                // This task was started because of movement of the activity based on affinity...
                // Now that we are actually launching it, we can assign the base intent.
                reusedActivity.getTask().setIntent(mStartActivity);
            }

            // This code path leads to delivering a new intent, we want to make sure we schedule it
            // as the first operation, in case the activity will be resumed as a result of later
            // operations.
            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
                final TaskRecord task = reusedActivity.getTask();

                // In this situation we want to remove all activities from the task up to the one
                // being started. In most cases this means we are resetting the task to its initial
                // state.
                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
                        mLaunchFlags);

                // The above code can remove {@code reusedActivity} from the task, leading to the
                // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
                // task reference is needed in the call below to
                // {@link setTargetStackAndMoveToFrontIfNeeded}.
                if (reusedActivity.getTask() == null) {
                    reusedActivity.setTask(task);
                }

                if (top != null) {
                    if (top.frontOfTask) {
                        // Activity aliases may mean we use different intents for the top activity,
                        // so make sure the task now has the identity of the new intent.
                        top.getTask().setIntent(mStartActivity);
                    }
                    deliverNewIntent(top);
                }
            }

            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);

            reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);

            final ActivityRecord outResult =
                    outActivity != null && outActivity.length > 0 ? outActivity[0] : null;

            // When there is a reused activity and the current result is a trampoline activity,
            // set the reused activity as the result.
            if (outResult != null && (outResult.finishing || outResult.noDisplay)) {
                outActivity[0] = reusedActivity;
            }

            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                // We don't need to start a new activity, and the client said not to do anything
                // if that is the case, so this is it!  And for paranoia, make sure we have
                // correctly resumed the top activity.
                resumeTargetStackIfNeeded();
                return START_RETURN_INTENT_TO_CALLER;
            }

            if (reusedActivity != null) {
                setTaskFromIntentActivity(reusedActivity);

                if (!mAddingToTask && mReuseTask == null) {
                    // We didn't do anything...  but it was needed (a.k.a., client don't use that
                    // intent!)  And for paranoia, make sure we have correctly resumed the top activity.

                    resumeTargetStackIfNeeded();
                    if (outActivity != null && outActivity.length > 0) {
                        outActivity[0] = reusedActivity;
                    }

                    return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
                }
            }
        }

        if (mStartActivity.packageName == null) {
            final ActivityStack sourceStack = mStartActivity.resultTo != null
                    ? mStartActivity.resultTo.getStack() : null;
            if (sourceStack != null) {
                sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
                        mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
                        null /* data */);
            }
            ActivityOptions.abort(mOptions);
            return START_CLASS_NOT_FOUND;
        }

        //SingleTop或则SingleInstance的处理
        // 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 ActivityStack topStack = mSupervisor.mFocusedStack;
        final ActivityRecord topFocused = topStack.getTopActivity();
        final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
        final boolean dontStart = top != null && mStartActivity.resultTo == null
                && top.realActivity.equals(mStartActivity.realActivity)
                && top.userId == mStartActivity.userId
                && top.app != null && top.app.thread != null
                && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
        if (dontStart) {
            // For paranoia, make sure we have correctly resumed the top activity.
            topStack.mLastPausedActivity = null;
            if (mDoResume) {
                mSupervisor.resumeFocusedStackTopActivityLocked();
            }
            ActivityOptions.abort(mOptions);
            if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                // We don't need to start a new activity, and the client said not to do
                // anything if that is the case, so this is it!
                return START_RETURN_INTENT_TO_CALLER;
            }

            deliverNewIntent(top);

            // Don't use mStartActivity.task to show the toast. We're not starting a new activity
            // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
            mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
                    preferredLaunchDisplayId, topStack);

            return START_DELIVERED_TO_TOP;
        }

        boolean newTask = false;
        final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                ? mSourceRecord.getTask() : null;

        // Should this be considered a new task?
        int result = START_SUCCESS;
        //设置对应的task并带到前台
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
        } else if (mSourceRecord != null) {
            result = setTaskFromSourceRecord();
        } else if (mInTask != null) {
            result = setTaskFromInTask();
        } else {
            // This not being started from an existing activity, and not part of a new task...
            // just put it in the top task, though these days this case should never happen.
            setTaskToCurrentTopOrCreateNewTask();
        }
        if (result != START_SUCCESS) {
            return result;
        }

        mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
        mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
                mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
        if (newTask) {
            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
                    mStartActivity.getTask().taskId);
        }
        ActivityStack.logStartActivity(
                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
        mTargetStack.mLastPausedActivity = null;

        mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
        //启动Activity
        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                mOptions);
        //使Activity可见
        if (mDoResume) {
            final ActivityRecord topTaskActivity =
                    mStartActivity.getTask().topRunningActivityLocked();
            if (!mTargetStack.isFocusable()
                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                    && mStartActivity != topTaskActivity)) {
                // If the activity is not focusable, we can't resume it, but still would like to
                // make sure it becomes visible as it starts (this will also trigger entry
                // animation). An example of this are PIP activities.
                // Also, we don't want to resume activities in a task that currently has an overlay
                // as the starting activity just needs to be in the visible paused state until the
                // over is removed.
                mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                // Go ahead and tell window manager to execute app transition for this activity
                // since the app transition will not be triggered through the resume channel.
                mService.mWindowManager.executeAppTransition();
            } else {
                // If the target stack was not previously focusable (previous top running activity
                // on that stack was not visible) then any prior calls to move the stack to the
                // will not update the focused stack.  If starting the new activity now allows the
                // task stack to be focusable, then ensure that we now update the focused stack
                // accordingly.
                if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
                    mTargetStack.moveToFront("startActivityUnchecked");
                }
                mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                        mOptions);
            }
        } else if (mStartActivity != null) {
            mSupervisor.mRecentTasks.add(mStartActivity.getTask());
        }
        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);

        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
                preferredLaunchDisplayId, mTargetStack);

        return START_SUCCESS;
    }
Copy the code

The whole process here is mainly the processing of the Activity startup mode 1. Calculate the DLAG of the startup 2. There are also special activity patterns and flag handling for finding tasks, as well as reuse of activities, including the following: LaunchSingleInstance LaunchSingleTask SingleTop && Activity is already at the top of the stack. If you specify the FLAG_ACTIVITY_NEW_TASK flag, Or the Activity mode to start is SingleInstance and SingleTask, or moption.getLaunchTasKid () specifies tasKid 3. Start a new task (FLAG_ACTIVITY_NEW_TASK is specified, FLAG_ACTIVITY_NEW_TASK is not specified in SourceRecord (the Activity that calls startActivity). And SourceRecord is not Null and starts in the specified task, and half is used to restore the task to start in the task in the current focus stack (neither FLAG_ACTIVITY_NEW_TASK nor SourceRecord) 4. Set up the corresponding task and brought to the front desk, to start the corresponding Activity (mTargetStack. StartActivityLocked). 5. Call mSupervisor. ResumeFocusedStackTopActivityLocked make visible Activity

Then look at the startActivityLocked method that starts the Activity, which is in ActivityStack, and see what happens

The loading of tasks and activities

ActivityStack.startActivityLocked

    // /frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
        TaskRecord rTask = r.getTask();
        final int taskId = rTask.taskId;
        // mLaunchTaskBehind tasks get placed at the back of the task stack.
        if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
            // Last activity in task had been removed or ActivityManagerService is reusing task.
            // Insert or replace.
            // Might not even be in.
            insertTaskAtTop(rTask, r);
        }
        TaskRecord task = null;
        if (!newTask) {
            // If starting in an existing task, find where that is...
            boolean startIt = true;
            for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
                task = mTaskHistory.get(taskNdx);
                if (task.getTopActivity() == null) {
                    // All activities in task are finishing.
                    continue;
                }
                if (task == rTask) {
                    // Here it is!  Now, if this is not yet visible to the
                    // user, then just add it without starting; it will
                    // get started when the user navigates back to it.
                    if (!startIt) {
                        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
                                + task, new RuntimeException("here").fillInStackTrace());
                        r.createWindowContainer();
                        ActivityOptions.abort(options);
                        return;
                    }
                    break;
                } else if (task.numFullscreen > 0) {
                    startIt = false;
                }
            }
        }

        //将一个新活动放在堆栈顶部,这样它就可以与用户进行交互
        // Place a new activity at top of stack, 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 TaskRecord activityTask = r.getTask();
        if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
            mStackSupervisor.mUserLeaving = false;
            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                    "startActivity() behind front, mUserLeaving=false");
        }

        task = activityTask;

        // Slot the activity into the history stack and proceed
        //将活动放入历史堆栈并继续
        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
                new RuntimeException("here").fillInStackTrace());
        // TODO: Need to investigate if it is okay for the controller to already be created by the
        // time we get to this point. I think it is, but need to double check.
        // Use test in b/34179495 to trace the call path.

        if (r.getWindowContainerController() == null) {
            r.createWindowContainer();
        }
        //设置栈为前台栈
        task.setFrontOfTask();

        //判断是否为第一个activity
        if (!isHomeOrRecentsStack() || numActivities() > 0) {
            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                    "Prepare open transition: starting " + r);
            //是否需要动画
            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
                mStackSupervisor.mNoAnimActivities.add(r);
            } else {
                int transit = TRANSIT_ACTIVITY_OPEN;
                if (newTask) {
                    if (r.mLaunchTaskBehind) {
                        transit = TRANSIT_TASK_OPEN_BEHIND;
                    } else {
                        // 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)
                        if (canEnterPipOnTaskSwitch(focusedTopActivity,
                                null /* toFrontTask */, r, options)) {
                            focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
                        }
                        transit = TRANSIT_TASK_OPEN;
                    }
                }
                //准备应用程序转换
                mWindowManager.prepareAppTransition(transit, keepCurTransition);
                mStackSupervisor.mNoAnimActivities.remove(r);
            }
            //是否显示启动窗口
            boolean doShow = true;
            if (newTask) {
                //如果一个Activity被设置了FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
                //并且是在一个新的task中启动,那么这个Activity就会被当作task的首个Activity来启动,即可能显示启动窗口
                // 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) {
                    resetTaskIfNeededLocked(r, r);
                    doShow = topRunningNonDelayedActivityLocked(null) == r;
                }
            } else if (options != null && options.getAnimationType()
                    == ActivityOptions.ANIM_SCENE_TRANSITION) {
                doShow = false;
            }
            if (r.mLaunchTaskBehind) {
                // Don't do a starting window for mLaunchTaskBehind. More importantly make sure we
                // tell WindowManager that r is visible even though it is at the back of the stack.
                r.setVisibility(true);
                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            } else if (SHOW_APP_STARTING_PREVIEW && doShow) {
                //此方法是寻找当前task中设置了STARTING_WINDOW_SHOWN
                //并且不是正在finishing中,并且可以显示的activity,STARTING_WINDOW_SHOWN
                //是在下面的showStartingWindow方法中设置的,也就是获取task中已添加了启动窗口的那个activity,
                //所以第一个启动的应用,他的task中没有设置STARTING_WINDOW_SHOWN的activity
                //最终prev为null
                // 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.
                TaskRecord prevTask = r.getTask();
                ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
                //如果prev不为Null说明此Activity在task中已经有了
                //其它Activity添加过了启动窗口
                if (prev != null) {
                    // We don't want to reuse the previous starting preview if:
                    // (1) The current activity is in a different task.
                    //如果当前正在启动的activity和已经添加了启动窗口的activity不属于同一个tsak,则让prev为Null
                    if (prev.getTask() != prevTask) {
                        prev = null;
                    }
                    // (2) The current activity is already displayed.
                    //当前正在启动的activity的nowVisible为true,则置空prev
                    else if (prev.nowVisible) {
                        prev = null;
                    }
                }
                //isTaskSwitch 方法判断(r, focusedTopActivity)是否在同一个task
                //focusedTopActivity当前系统最顶层的activity
                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
            }
        } else {
            //第一个activity不需要任何花里胡哨的动画
            // If this is the first activity, don't do any fancy animations,
            // because there is nothing for it to animate on top of.
            ActivityOptions.abort(options);
        }
    }
Copy the code

The main function of this method is to stack tasks and activities, create a Task for a newly started Activity, and save the Task in ActivityRecord

  1. Check whether it is a newly created task based on newTask. Otherwise, the task is retrieved from the history stack
  2. Set the stack to the foreground stack
  3. Determine if animation is required and prepare the application for conversion
  4. Setting up the startup window

Then come back read mSupervisor resumeFocusedStackTopActivityLocked

ActivityStackSupervisor.resumeFocusedStackTopActivityLocked

// /frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { if (! readyToResume()) { return false; } // Check if the activity (targetStack!) is not null and is in focus. If (targetStack! = null && isFocusedStack(targetStack)) { return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); } final ActivityRecord r = mFocusedStack.topRunningActivityLocked(); if (r == null || ! r.isState(RESUMED)) { mFocusedStack.resumeTopActivityUncheckedLocked(null, null); } else if (r.isState(RESUMED)) { // Kick off any lingering app transitions form the MoveTaskToFront operation. mFocusedStack.executeAppTransition(targetOptions); } return false; }Copy the code

This method is mainly to check the focus of the Activity stack, then call targetStack. ResumeTopActivityUncheckedLocked continue processing

ActivityStack.resumeTopActivityUncheckedLocked

// /frameworks/base/services/core/java/com/android/server/am/ActivityStack.java @GuardedBy("mService") boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) { if (mStackSupervisor.inResumeTopActivity) { // Don't even start recursing. return false; } boolean result = false; try { // Protect against recursion. mStackSupervisor.inResumeTopActivity = true; / / the Activity start the core method of result = resumeTopActivityInnerLocked (prev, options); // When resuming the top activity, it may be necessary to pause the top activity (for // example, returning to the lock screen. We suppress the normal pause logic in // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here // to ensure any necessary pause logic occurs. In the case where the Activity will be // shown regardless of the lock screen, the call to // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped. final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); if (next == null || ! next.canTurnScreenOn()) { checkReadyForSleep(); } } finally { mStackSupervisor.inResumeTopActivity = false; } return result; }Copy the code

To call this place resumeTopActivityInnerLocked method, here is divided into several parts, the first is to suspend the current running Activity

Pause operation

ActivityStack. ResumeTopActivityInnerLocked (upper part Pause the Activity entry)

    // /frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
    @GuardedBy("mService")
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        if (!mService.mBooting && !mService.mBooted) {
            // Not ready yet!
            return false;
        }

        // Find the next top-most activity to resume in this stack that is not finishing and is
        // focusable. If it is not focusable, we will fall into the case below to resume the
        // top activity in the next focusable task.
        //将要显示的目标activity
        final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);

        final boolean hasRunningActivity = next != null;

        // TODO: Maybe this entire condition can get removed?
        if (hasRunningActivity && !isAttached()) {
            return false;
        }

        //非顶部处于INITIALIZING状态的活动清理
        mStackSupervisor.cancelInitializingActivities();

        // Remember how we'll process this pause/resume situation, and ensure
        // that the state is reset however we wind up proceeding.
        //标记是否调用Activity的performUserLeaving回调方法
        boolean userLeaving = mStackSupervisor.mUserLeaving;
        mStackSupervisor.mUserLeaving = false;

        if (!hasRunningActivity) {
            // There are no activities left in the stack, let's look somewhere else.
            return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
        }

        //标记是否延迟显示
        next.delayedResume = false;

        // If the top activity is the resumed one, nothing to do.
        //如果最重要的activity是恢复的activity,则无需执行任何操作
        if (mResumedActivity == next && next.isState(RESUMED)
                && mStackSupervisor.allResumedActivitiesComplete()) {
            // Make sure we have executed any pending transitions, since there
            // should be nothing left to do at this point.
            executeAppTransition(options);
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Top activity resumed " + next);
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;
        }

        // If we are sleeping, and there is no resumed activity, and the top
        // activity is paused, well that is the state we want.
        //如果activity sleep或则已暂停,则是我们需要的状态,不需要再处理
        if (shouldSleepOrShutDownActivities()
                && mLastPausedActivity == next
                && mStackSupervisor.allPausedActivitiesComplete()) {
            // Make sure we have executed any pending transitions, since there
            // should be nothing left to do at this point.
            executeAppTransition(options);
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Going to sleep and all paused");
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;
        }

        // Make sure that the user who owns this activity is started.  If not,
        // we will just leave it as is because someone should be bringing
        // another user's activities to the top of the stack.
        //确保负责此activity的用户已启动。如果不是,
        //我们将保持原样,因为应该有人带来
        //将另一个用户的活动添加到堆栈顶部。
        if (!mService.mUserController.hasStartedUserState(next.userId)) {
            Slog.w(TAG, "Skipping resume of top activity " + next
                    + ": user " + next.userId + " is stopped");
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;
        }

        // The activity may be waiting for stop, but that is no longer
        // appropriate for it.
        //如果该activity正在等待或则停止,则移除
        mStackSupervisor.mStoppingActivities.remove(next);
        mStackSupervisor.mGoingToSleepActivities.remove(next);
        next.sleeping = false;
        mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next);

        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);

        // If we are currently pausing an activity, then don't do anything until that is done.
        //判断当前是否存在暂停未完成的activity
        if (!mStackSupervisor.allPausedActivitiesComplete()) {
            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                    "resumeTopActivityLocked: Skip resume: some activity pausing.");
            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
            return false;
        }

        //设置wake lock关联的Source,用于应用耗电统计
        mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);

        //画中画判断
        boolean lastResumedCanPip = false;
        ActivityRecord lastResumed = null;
        final ActivityStack lastFocusedStack = mStackSupervisor.getLastStack();
        if (lastFocusedStack != null && lastFocusedStack != this) {
            // So, why aren't we using prev here??? See the param comment on the method. prev doesn't
            // represent the last resumed activity. However, the last focus stack does if it isn't null.
            lastResumed = lastFocusedStack.mResumedActivity;
            if (userLeaving && inMultiWindowMode() && lastFocusedStack.shouldBeVisible(next)) {
                // The user isn't leaving if this stack is the multi-window mode and the last
                // focused stack should still be visible.
                if(DEBUG_USER_LEAVING) Slog.i(TAG_USER_LEAVING, "Overriding userLeaving to false"
                        + " next=" + next + " lastResumed=" + lastResumed);
                userLeaving = false;
            }
            lastResumedCanPip = lastResumed != null && lastResumed.checkEnterPictureInPictureState(
                    "resumeTopActivity", userLeaving /* beforeStopping */);
        }
        // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
        // to be paused, while at the same time resuming the new resume activity only if the
        // previous activity can't go into Pip since we want to give Pip activities a chance to
        // enter Pip before resuming the next activity.
        final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
                && !lastResumedCanPip;
        //pausing标记是否有执行pause操作
        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
        //mResumedActivity表示正在显示的activity,若不为null,则暂停它startPausingLocked
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, next, false);
        }
        //省略
        ...
    }
Copy the code

ActivityStack.startPausingLocked

StartPausingLocked will pause an Activity that is currently running. Let’s look at this method

// /frameworks/base/services/core/java/com/android/server/am/ActivityStack.java final boolean startPausingLocked(boolean  userLeaving, boolean uiSleeping, ActivityRecord resuming, Boolean pauseImmediately) {//mPausingActivity specifies the activity currently being paused. If (mPausingActivity! = null) { Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity + " state=" + mPausingActivity.getState()); if (! shouldSleepActivities()) { // Avoid recursion among check for sleep and complete pause during sleeping. // Because activity will be paused immediately after resume, just let pause // be completed by the order of activity paused from clients. completePauseLocked(false, resuming); }} // Check prev ActivityRecord prev = mResumedActivity; if (prev == null) { if (resuming == null) { Slog.wtf(TAG, "Trying to pause when nothing is resumed"); mStackSupervisor.resumeFocusedStackTopActivityLocked(); } return false; } if (prev == resuming) { Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed"); return false; } // Assign prev to mPausingActivity, If (DEBUG_STATES) slog. v(TAG_STATES, "Moving to PAUSING: "+ prev); else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev); mPausingActivity = prev; mLastPausedActivity = prev; mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) ! = 0 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) ! = 0? prev : null; // Set the prev state to PAUSING, which sets mResumedActivity to null prev.setState(PAUSING, "startPausingLocked"); // Update the last active time of prev's task prev.gettask ().touchActiveTime(); // Set timeout to clearLaunchTime(prev); mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode()); mService.updateCpuStats(); If (prev. App! = null && prev.app.thread ! = null) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); try { EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev), prev.shortComponentName, "userLeaving=" + userLeaving); mService.updateUsageStats(prev, false); / / notify the target application process corresponding to the life of the Activity scheduling mService. GetLifecycleManager () scheduleTransaction (prev. App. Thread, prev. AppToken, PauseActivityItem.obtain(prev.finishing, userLeaving, prev.configChangeFlags, pauseImmediately)); } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Slog.w(TAG, "Exception thrown during pause", e); mPausingActivity = null; mLastPausedActivity = null; mLastNoHistoryActivity = null; } } else { mPausingActivity = null; mLastPausedActivity = null; mLastNoHistoryActivity = null; } // If we are not going to sleep, we want to ensure the device is // awake until the next activity is started. if (! uiSleeping && ! mService.isSleepingOrShuttingDownLocked()) { mStackSupervisor.acquireLaunchWakelock(); } // Normally mPausingActivity holds a reference in if (mPausingActivity! = null) { // Have the window manager pause its key dispatching until the new // activity has started. If we're pausing the activity just because // the screen is being turned off and the UI is sleeping, don't interrupt // key dispatch; the same activity will pick it up again on wakeup. if (! UiSleeping) {/ / pause event distribution prev. PauseKeyDispatchingLocked (); } else if (DEBUG_PAUSE) { Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off"); } if (pauseImmediately) { // If the caller said they don't want to wait for the pause, then complete // the pause now. completePauseLocked(false, resuming); return false; } else { schedulePauseTimeout(prev); return true; } } else { // This activity failed to schedule the // pause, so just treat it as being paused now. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next."); / / resuming the default holds the next to display activityRecord if (resuming = = null) {mStackSupervisor. ResumeFocusedStackTopActivityLocked (); } return false; }}Copy the code

This method assigns mResumedActivity to mPausingActivity and sets the state to PAUSING, The mResumedActivity is then set to null and the corresponding App is notified by IApplicationThread to perform the pause operation. The scheduleTransaction method continues

ClientLifecycleManager.scheduleTransaction

// /frameworks/base/services/core/java/com/android/server/am/ClientLifecycleManager.java void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken, @nonnull ActivityLifecycleItem stateRequest) throws RemoteException { Save the client, token, final stateRequest parameters ClientTransaction ClientTransaction = transactionWithState (client, activityToken. stateRequest); scheduleTransaction(clientTransaction); }Copy the code

ClientTransaction transaction object that encapsulates commands and parameters and sends them to the APP process for processing. In ClientTransaction, the scheduleTransaction method of IApplicationThread is called. Moving on to scheduleTransaction,AMS is called to the APP process via IApplicationThread. ScheduleTransaction triggers ApplicationThread scheduleTransaction method, This method also calls the scheduleTransaction method of ActivityThread’s parent class ClientTransactionHandler

ClientTransactionHandler.scheduleTransaction

    // /frameworks/base/core/java/android/app/ClientTransactionHandler.java
    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }
Copy the code

The abstract method sendMessage is implemented in ActivityThread

ActivityThread.sendMessage

// /frameworks/base/core/java/android/app/ActivityThread.java private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { if (DEBUG_MESSAGES) Slog.v( TAG, "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj); Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous(true); } mH.sendMessage(msg); } to omit... case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; mTransactionExecutor.execute(transaction); if (isSystem()) { // Client transactions inside system process are recycled on the client side // instead of ClientLifecycleManager to avoid being cleared before this // message is handled. transaction.recycle(); } // TODO(lifecycler): Recycle locally scheduled transactions. break;Copy the code

In this approach, mH is used to switch from the binder thread to the main thread to handle the transaction. ClientTransaction transaction messages are executed through TransactionExecutor’s execute method in the corresponding case EXECUTE_TRANSACTION in mH’s handleMessage method.

TransactionExecutor.execute

// /frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java public void execute(ClientTransaction transaction) { final IBinder token = transaction.getActivityToken(); log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token); // Iterate over the mActivityCallbacks in ClientTransaction executeCallbacks(Transaction); // Call the mLidecycleStateRequest member in ClientTransaction, which is the PauseActivityItem executeLifecycleState(Transaction) set in Ams; mPendingActions.clear(); log("End resolving transaction"); } @VisibleForTesting public void executeCallbacks(ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null) { // No callbacks to execute, return early. return; } log("Resolving callbacks"); final IBinder token = transaction.getActivityToken(); ActivityClientRecord r = mTransactionHandler.getActivityClient(token); // In case when post-execution state of the last callback matches the final state requested // for the activity in this transaction, we won't do the last transition here and do it when // moving to final state instead (because it may contain additional parameters from server). final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest(); final int finalState = finalStateRequest ! = null ? finalStateRequest.getTargetState() : UNDEFINED; // Index of the last callback that requests some post-execution state. final int lastCallbackRequestingState = lastCallbackRequestingState(transaction); final int size = callbacks.size(); for (int i = 0; i < size; ++i) { final ClientTransactionItem item = callbacks.get(i); log("Resolving callback: " + item); final int postExecutionState = item.getPostExecutionState(); final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r, item.getPostExecutionState()); if (closestPreExecutionState ! = UNDEFINED) { cycleToPath(r, closestPreExecutionState); } item.execute(mTransactionHandler, token, mPendingActions); item.postExecute(mTransactionHandler, token, mPendingActions); if (r == null) { // Launch activity request will create an activity record. r = mTransactionHandler.getActivityClient(token); } if (postExecutionState ! = UNDEFINED && r ! = null) { // Skip the very last transition and perform it by explicit state request instead. final boolean shouldExcludeLastTransition = i == lastCallbackRequestingState && finalState == postExecutionState; cycleToPath(r, postExecutionState, shouldExcludeLastTransition); } } } /** Transition to the final state if requested by the transaction. */ private void executeLifecycleState(ClientTransaction transaction) { final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest(); if (lifecycleItem == null) { // No lifecycle request, return early. return; } log("Resolving lifecycle state: " + lifecycleItem); final IBinder token = transaction.getActivityToken(); final ActivityClientRecord r = mTransactionHandler.getActivityClient(token); if (r == null) { // Ignore requests for non-existent client records for now. return; } // Cycle to the state right before the final requested state. // This method will complement and execute the corresponding lifecycle phases of the method in the lifecycle order cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */); // Execute the final transition with proper parameters. lifecycleItem.execute(mTransactionHandler, token, mPendingActions); lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions); }Copy the code

As with ExecutelifeccleState, the Execute and postExecute methods of PauseActivityItem are called. Will call the execute ClientTransactionHandler handlePauseActivity method, PauseActivityItem. Execute

    // /frameworks/base/core/java/android/app/servertransaction/PauseActivityItem.java
    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
                "PAUSE_ACTIVITY_ITEM");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
Copy the code

HandlePauseActivity is implemented by ActivityThread

ActivityThread.handlePauseActivity

// /frameworks/base/core/java/android/app/ActivityThread.java @Override public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, PendingTransactionActions pendingActions, String reason) { ActivityClientRecord r = mActivities.get(token); if (r ! = null) { if (userLeaving) { performUserLeavingActivity(r); } r.activity.mConfigChangeFlags |= configChanges; // Execute pause PauseActivity performPauseActivity(r, finished, Reason, pendingActions); // Make sure any pending writes are now committed. if (r.isPreHoneycomb()) { QueuedWork.waitToFinish(); } mSomeActivitiesChanged = true; } } private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, PendingTransactionActions pendingActions) { if (r.paused) { if (r.activity.mFinished) { // If we are finishing, we won't call onResume() in certain cases. // So here we likewise don't want to call onPause() if the activity // isn't resumed. return null; } RuntimeException e = new RuntimeException( "Performing pause of activity that is not resumed: " + r.intent.getComponent().toShortString()); Slog.e(TAG, e.getMessage(), e); } if (finished) { r.activity.mFinished = true; } // Pre-Honeycomb apps always save their state before pausing final boolean shouldSaveState = ! r.activity.mFinished && r.isPreHoneycomb(); if (shouldSaveState) { callActivityOnSaveInstanceState(r); } performPauseActivityIfNeeded(r, reason); // Notify any outstanding on paused listeners ArrayList<OnActivityPausedListener> listeners; synchronized (mOnPauseListeners) { listeners = mOnPauseListeners.remove(r.activity); } int size = (listeners ! = null ? listeners.size() : 0); for (int i = 0; i < size; i++) { listeners.get(i).onPaused(r.activity); } final Bundle oldState = pendingActions ! = null ? pendingActions.getOldState() : null; if (oldState ! = null) { // We need to keep around the original state, in case we need to be created again. // But we only do this for pre-Honeycomb apps, which always save their state when // pausing, so we can not have them save their state when restarting from a paused // state. For HC and later, we want to (and can) let the state be saved as the // normal part of stopping the activity. if (r.isPreHoneycomb()) { r.state = oldState; } } return shouldSaveState ? r.state : null; } private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) { if (r.paused) { // You are already paused silly... return; } try { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); if (! r.activity.mCalled) { throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onPause()"); } } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (! mInstrumentation.onException(r.activity, e)) { throw new RuntimeException("Unable to pause activity " + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } r.setState(ON_PAUSE); }Copy the code

The state saved in ActivityClientRecord is checked in the performPauseActivity method and onSaveInstanceState() is called as appropriate, Then call performPauseActivityIfNeeded method, in this method by Instrumentation eventually triggered the Activity’s onPause lifecycle callback methods.

When this is done, the postExecute method of the PauseActivityItem is then executed

PauseActivityItem.postExecute

// /frameworks/base/core/java/android/app/servertransaction/PauseActivityItem.java @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { if (mDontReport) { return; } try { // TODO(lifecycler): Use interface callback instead of AMS. ActivityManager.getService().activityPaused(token); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); }}Copy the code

Then call AMS’s activityPaused to notify the Activity Pause that it has finished

ActivityManagerService.activityPaused

// /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java @Override public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized(this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack ! = null) { stack.activityPausedLocked(token, false); } } Binder.restoreCallingIdentity(origId); }Copy the code

This calls activityPausedLocked of ActivityStack

ActivityStack.activityPausedLocked

// /frameworks/base/services/core/java/com/android/server/am/ActivityStack.java final void activityPausedLocked(IBinder token, boolean timeout) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity paused: token=" + token + ", timeout=" + timeout); final ActivityRecord r = isInStackLocked(token); if (r ! = null) { mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); if (mPausingActivity == r) { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r + (timeout ? " (due to timeout)" : " (pause complete)")); mService.mWindowManager.deferSurfaceLayout(); try { completePauseLocked(true /* resumeNext */, null /* resumingActivity */); } finally { mService.mWindowManager.continueSurfaceLayout(); } return; } else { EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, r.userId, System.identityHashCode(r), r.shortComponentName, mPausingActivity ! = null ? mPausingActivity.shortComponentName : "(none)"); if (r.isState(PAUSING)) { r.setState(PAUSED, "activityPausedLocked"); if (r.finishing) { if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of failed to pause activity: " + r); finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false, "activityPausedLocked"); } } } } mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, ! PRESERVE_WINDOWS); }Copy the code

This is going to call the completePauseLocked method again

ActivityStack.com pletePauseLocked (Pasue Activity complete)

// /frameworks/base/services/core/java/com/android/server/am/ActivityStack.java private void completePauseLocked(boolean ResumeNext, ActivityRecord) {assign PauseActivity to prev ActivityRecord prev = mPausingActivity; if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev); if (prev ! = null) { prev.setWillCloseOrEnterPip(false); final boolean wasStopping = prev.isState(STOPPING); prev.setState(PAUSED, "completePausedLocked"); If (prev. Finishing) {// Finish Activity if (DEBUG_PAUSE) slog. v(TAG_PAUSE, "Executing of Activity: "+ prev); prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false, "completedPausedLocked"); } else if (prev.app ! = null) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev + " wasStopping=" + wasStopping + " visible=" + prev.visible); if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(prev)) { if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause, no longer waiting: " + prev); } if (prev.deferRelaunchUntilPaused) { // Complete the deferred relaunch that was waiting for pause to complete. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev); prev.relaunchActivityLocked(false /* andResume */, prev.preserveWindowOnDeferredRelaunch); } else if (wasStopping) { // We are also stopping, the stop request must have gone soon after the pause. // We can't clobber it, because the stop confirmation will not be handled. // We don't need to schedule another stop, we only need to let it happen. prev.setState(STOPPING, "completePausedLocked"); } else if (! prev.visible || shouldSleepOrShutDownActivities()) { // Clear out any deferred client hide we might currently have. prev.setDeferHidingClient(false); // If we were visible then resumeTopActivities will release resources before // stopping addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */); } } else { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev); prev = null; } // It is possible the activity was freezing the screen before it was paused. // In that case go ahead and remove the freeze this activity has on the screen // since it is no longer visible. if (prev ! = null) {/ / stop screen freezes prev stopFreezingScreenLocked force (true / * * /); } mPausingActivity = null; } / / said that need to be an Activity scheduling resume if (resumeNext) {final ActivityStack topStack = mStackSupervisor. GetFocusedStack (); if (! topStack.shouldSleepOrShutDownActivities()) { mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null); } else { checkReadyForSleep(); ActivityRecord top = topStack.topRunningActivityLocked(); if (top == null || (prev ! = null && top ! = prev)) { // If there are no more activities available to run, do resume anyway to start // something. Also if the top activity on the stack is not the just paused // activity, we need to go ahead and resume it to ensure we complete an // in-flight app switch. mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } } if (prev ! = null) { prev.resumeKeyDispatchingLocked(); if (prev.app ! = null && prev.cpuTimeAtResume > 0 && mService.mBatteryStatsService.isOnBattery()) { long diff = mService.mProcessCpuTracker.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume; if (diff > 0) { BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics(); synchronized (bsi) { BatteryStatsImpl.Uid.Proc ps = bsi.getProcessStatsLocked(prev.info.applicationInfo.uid, prev.info.packageName); if (ps ! = null) { ps.addForegroundTimeLocked(diff); } } } } prev.cpuTimeAtResume = 0; // reset it } // Notify when the task stack has changed, but only if visibilities changed (not just // focus). Also if there is an active pinned stack - we always want to notify  it about // task stack changes, because its positioning may depend on it. if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause || getDisplay().hasPinnedStack()) { mService.mTaskChangeNotificationController.notifyTaskStackChanged(); mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false; } mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, ! PRESERVE_WINDOWS); }Copy the code

The completePauseLocked method stores the pause ActivityRecord to the mStoppingActivities member (ArrayList) of the ActivityStackSupervisor. It is used to stop and finish it later. MPausingActivity is then assigned to null, indicating that no Activity is currently being paused. And then again call resumeFocusedStackTopActivityLocked method, again to resume Activity.

It is divided into two parts due to length. Click to continue the Activity startup process (below)