ActivityManagerService解读之Activity启动初探_activitytaskmanager: start u0 {act=android.intent.-程序员宅基地

技术标签: ActivityManagerService  Activity启动  

Activity是Android四大组建之一,负责用户交互界面展示,其重要性不可言喻。Android系统由ActivityManagerService负责管理Activity。熟悉Activity的启动,将对我们学习了解ActivityManagerService大有裨益。本文基于AndroidP将以首次点击桌面应用图标的方式为线,分析Activity的启动,包括分析应用进程启动。文中将涉及不少应用启动的字眼,为了避免理解上的误解,先做一解释,其实应用启动的过程,便会伴随着Activity的启动,分析应用的启动流程包括Activity的启动。这里不讨论无界面应用(比如只托管service的应用进程启动),特指有界面的应用程序。由于篇幅原因文章中,只列举一些启动过程中的关键代码,将会更多介绍AMS相关的内容,至于涉及到WMS等其他内容暂不做重点讨论。我们在理解这些关键的基础上,回头再看Activity的启动将“如履平地”般容易。

以小见大-以Log窥Activity启动全貌(Log来源小米8手机Android8.1系统,与P相差不大)

09-19 15:58:37.864  1311  3040 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.tencent.mobileqq/.activity.SplashActivity bnds=[329,1575][497,1743] (has extras)} from uid 10032
09-19 15:58:37.879  1311  3040 I wm_task_created: [6415,1]//来自TaskWindowContainerController构造方法,
09-19 15:58:37.880  1311  3040 I wm_task_moved: [6415,1,9]//来自TaskWindowContainerController构造方法,具体是在TaskStack中打出的,因为这里调用了stack.addTask
09-19 15:58:37.880  1311  3040 I am_focused_stack: [0,1,0,reuseOrNewTask]
09-19 15:58:37.880  1311  3040 I wm_task_moved: [6415,1,9]//会做一次位置调整addOrReparentStartingActivity,来自于此,之后还会移动一次ActivityStack
09-19 15:58:37.882  1311  3040 I am_create_task: [0,6415]//打印出如下的Log的时候,此时ams启动Activity已经就该应用Activity创建了属于他的TaskRecord
09-19 15:58:37.882  1311  3040 I am_create_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,android.intent.action.MAIN,NULL,NULL,270532608]
09-19 15:58:37.882  1311  3040 I wm_task_moved: [6415,1,9]//ActivityStack.startActivityLocked 先做一次ActivityStack的移动
09-19 15:58:37.885  1311  3040 I am_pause_activity: [3167,192963549,com.miui.home/.launcher.Launcher] //启动一个Activity之前先暂停之前显示的Activity
09-19 15:58:37.888  3167  3167 I am_on_paused_called: [0,com.miui.home.launcher.Launcher,handlePauseActivity]
09-19 15:58:37.888  1311  3311 I am_uid_running: 10158
09-19 15:58:37.900  1311  3311 I am_proc_start: [0,31868,10158,com.tencent.mobileqq,activity,com.tencent.mobileqq/.activity.SplashActivity]//来自ActivityManagerService startProcessLocked
09-19 15:58:37.905  1311  3311 I am_proc_bound: [0,31868,com.tencent.mobileqq]//来自ActivityManagerService  attachApplicationLocked 当系统创建好系统应用进程时,
                                                                              //会调用ActivityThread的main方法,此时会通过Binder调用ams的attachApplication,进而调用attachApplicationLocked
09-19 15:58:37.909  1311  3311 I am_restart_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,31868]
09-19 15:58:37.909  1311  3311 I am_restart_activity_ai: [31868,com.tencent.mobileqq/.activity.SplashActivity,false]//xmiaomi特有AI启动?黑科技?
09-19 15:58:37.912  1311  3311 I am_set_resumed_activity: [0,com.tencent.mobileqq/.activity.SplashActivity,minimalResumeActivityLocked]
09-19 15:58:38.045 31868 31868 I am_on_resume_called: [0,com.tencent.mobileqq.activity.SplashActivity,LAUNCH_ACTIVITY]//应用进程Log输出
09-19 15:58:38.104  1311  1388 I am_activity_launch_time: [0,72463824,com.tencent.mobileqq/.activity.SplashActivity,213,213]
09-19 15:58:38.426  1311  1327 I am_stop_activity: [0,192963549,com.miui.home/.launcher.Launcher]
09-19 15:58:38.436  3167  3167 I am_on_stop_called: [0,com.miui.home.launcher.Launcher,handleStopActivity]

以上这份Log基本可以说是涵盖了Acitivity启动过程中所有的关键点信息,作为初学者,可能不知所以然,不过没关系,本文将详细介绍,这其中的原理。你可以获得Activity启动的耗时时间,你可以获得Activity启动所在的Stack和归属的Task id号……

基础普及-一张小图解Activity

State diagram for an Android Activity Lifecycle.

Activity 生命周期回调方法汇总表。 

方法 说明 是否能事后终止? 后接
onCreate 首次创建 Activity 时调用。 您应该在此方法中执行所有正常的静态设置 — 创建视图、将数据绑定到列表等等。 系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态(请参阅后文的保存 Activity 状态)。

始终后接 onStart()。
NO onStart
onReStart 在 Activity 已停止并即将再次启动前调用。

始终后接 onStart()
NO onStart
onStart 在 Activity 即将对用户可见之前调用。
如果 Activity 转入前台,则后接 onResume(),如果 Activity 转入隐藏状态,则后接 onStop()。 
NO onResume

onStop
onResume 在 Activity 即将开始与用户进行交互之前调用。 此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。
始终后接 onPause()。
NO onPause
onPause 当系统即将开始继续另一个 Activity 时调用。 此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。
如果 Activity 返回前台,则后接 onResume(),如果 Activity 转入对用户不可见状态,则后接 onStop()。
YES onResume

onStop
onStop 在 Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。
如果 Activity 恢复与用户的交互,则后接 onRestart(),如果 Activity 被销毁,则后接 onDestroy()。 
YES onRestart

onDestroy
onDestory 在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 当 Activity 结束(有人对 Activity 调用了 finish()),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 您可以通过 isFinishing() 方法区分这两种情形。 YES

了解Activity,就要知道Activity的生命周期(Lifecycle),知道Activity各个生命周期出现的场景。如图所示,Activity从开始到结束可细分为:onCreate,onStart,onResume,onPause,onStop,onDestory。Activity的启动方式,显示,隐式:

Intent explicitIntent = new Intent("i.am.a.explicit.start");
getBaseContext().startActivity(explicitIntent);

Intent implicitIntent = new Intent(getBaseContext(), MainActivity.class);
getBaseContext().startActivity(implicitIntent);

一般我们都是如此启动,当然我们也可以通过配置AndroidManifest我们的某个Activity作为应用启动首选Activity:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

 Android规定Activity有四种启动方式standard,singleTop,singleTask,singleInstance。

Android应用程序的入口ActivityThread-好比Java程序的main主函数

讲Activity启动之前,必须强调一下ActivityThread,ActivityThread的main方法便是Android应用程序的入口,代表应用程序执行开始。

    public static void main(String[] args) {
        Process.setArgV0("<pre-initialized>");
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Android应用程序的事件操作是基于消息机制的,简单概述一下Android的消息机制,Android消息机制三大核心Handler,MessageQueue,Loop。Handler负责处理消息,MessageQueue用来保存消息,Loop用来循环取消息。ActivityThread的main方法的最后一行Looper.loop()(这是一个死循环,后续介绍Android消息机制的时候详细解释)意思就是我们的应用进入工作循环模式,开始运行,准备接受处理消息了。直白的说就是应用跑起来了。既然应用程序已经运行起来了,那么我们就需要Activity界面展示了。我们就要启动我们的Activity了,本文将以首次点击QQ桌面图标启动QQ首Activity为线,来分析Activity的启动流程及原理。

弹指一击QQ应用桌面图标-手机系统都经历了啥?

当我们首次通过点击QQ桌面图标来启动QQ应用时,QQ启动的动作请求来自桌面应用miui Launcher,经过一些列的调用,便请求AMS启动QQ应用,最终调用AMS的ActivityStarter的startActivity方法,该方法可以认为是Activity启动过程中的第一步,负责启动时最基本的检查操作,比如检查Activity信息是否存在,创建ActivityRecord等:

    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
            SafeActivityOptions options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) {
        int err = ActivityManager.START_SUCCESS;
        // Pull the optional Ephemeral Installer-only bundle out of the options early.
        final Bundle verificationBundle
                = options != null ? options.popAppVerificationBundle() : null;

        ProcessRecord callerApp = null;//启动Activity动作的发起进程,本例中之miui Launcher
        if (caller != null) {
            callerApp = mService.getRecordForAppLocked(caller);
            if (callerApp != null) {
                callingPid = callerApp.pid;
                callingUid = callerApp.info.uid;
            } else {
                Slog.w(TAG, "Unable to find app for caller " + caller
                        + " (pid=" + callingPid + ") when starting: "
                        + intent.toString());
                err = ActivityManager.START_PERMISSION_DENIED;
            }
        }

        final int userId = aInfo != null && aInfo.applicationInfo != null
                ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;

        //这就是我们看见的第一行Log
        if (err == ActivityManager.START_SUCCESS) {
            Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
                    + "} from uid " + callingUid);
        }

        ActivityRecord sourceRecord = null;//startActivityForResult方式启动时,需要记录启动Activity,可以理解为call activity
        ActivityRecord resultRecord = null;//startActivityForResult方式启动时,记录setResult最终返回的Activity
        if (resultTo != null) {//当使用startActivityForResult方式启动时,resultTo指的是启动Activity
            sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                    "Will send result to " + resultTo + " " + sourceRecord);
            if (sourceRecord != null) {
                if (requestCode >= 0 && !sourceRecord.finishing) {
                    //一段美妙的逻辑,很有意思,借助requestCode来解决冲突
                    //默认我们通过startActivityForResult方式启动时,requestCode都会>=0
                    resultRecord = sourceRecord;//此时call activity = setResult最终返回的Activity
                }
            }
        }

        final int launchFlags = intent.getFlags();//获取启动的flags信息

        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
            // Transfer the result target from the source activity to the new
            // one being started, including any failures.
            //一段美妙的逻辑,很有意思,借助requestCode来解决冲突,规定使用FLAG_ACTIVITY_FORWARD_RESULT flag时,
            //比如A---startActivityForResult--requestCode>=0--->B---startActivityForResult--+FLAG_ACTIVITY_FORWARD_RESULT--requestCode<0--->C
            //B的参数requestCode必须<=0,此时CsetResult的结果返回给A,这就是FLAG_ACTIVITY_FORWARD_RESULT的用法
            if (requestCode >= 0) {
                SafeActivityOptions.abort(options);
                return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
            }
            resultRecord = sourceRecord.resultTo;//B的resultTo是A,获取A
            if (resultRecord != null && !resultRecord.isInStackLocked()) {
                resultRecord = null;
            }
            resultWho = sourceRecord.resultWho;
            requestCode = sourceRecord.requestCode;
            sourceRecord.resultTo = null;
            if (resultRecord != null) {
                resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
            }
        //此后的一部分代码略去,他们的工作主要用来设置一些resultActivity的信息,检查启动Activity是否合法,系统时候存在Activity相关信息,当这些条件都满足的时候就进行下一步,创建ActivityRecord

        //为将要启动的Activity创建一个ActivityRecord
        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                mSupervisor, checkedOptions, sourceRecord);

        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true /* doResume */, checkedOptions, inTask, outActivity);
    }

该方法执行会输出对应Log

09-19 15:58:37.864  1311  3040 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.tencent.mobileqq/.activity.SplashActivity bnds=[329,1575][497,1743] (has extras)} from uid 10032

经过层层调用便会继续调用ActivityStarter的startActivityUnchecked方法,从命名可以看出,该方法中应该不做任何检查工作,之负责Activity启动时在AMS中的一些初始化的任务,我们知道Activity启动涉及启动flag,Task,Stack信息,我们需要做一个初始化:

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {

        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);//设置当前启动的Activity信息

        computeLaunchingTaskFlags();//Android中一些Intent.flag会决定是否新建task,重新计算一下相关参数

        computeSourceStack();//Android中规定Activity依附Task,Task存在于Stack中,Android存在好几种Stack

        mIntent.setFlags(mLaunchFlags);

        ActivityRecord reusedActivity = getReusableIntentActivity();//首次启动时为null

 startActivityUnchecked方法还会为启动的Activity创建或复用对应的Stack和Task。更多Stack和Task信息请查看文章“任务(Task)和堆栈(Stack)”google网站翻译内容,介绍的非常详细。

        / 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;//获取当前获得焦点的Stack,由于启动时显示的是miui Launcher本里,就是miui Launcher所在的Stack
        final ActivityRecord topFocused = topStack.getTopActivity();//获取当前Stack中有焦点的Activity,可以说是当前显示的Activity
        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;
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            newTask = true;
            String packageName= mService.mContext.getPackageName();
            if (mPerf != null) {
                mStartActivity.perfActivityBoostHandler =
                    mPerf.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST,
                                        packageName, -1, BoostFramework.Launch.BOOST_V1);
            }
            //创建Task,或者依据taskToAffiliate进行复用,本例是新建taskToAffiliate=null
            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;
        }

调用setTaskFromReuseOrCreateNewTask创建或者复用当前的启动Activity所在的Task,本例是创建。当应用内部Activity跳转排除启动模式和启动Flags干扰时,一般会复用之前Activity所在的Task。创建TaskRecod由ActivityStack.createTaskRecord完成,期间还会调用TaskRecord.createWindowContainer创建对应的TaskWindowContainerController,同时关联TaskRecord和TaskWindowContainerController,对应如下Log:

09-19 15:58:37.879  1311  3040 I wm_task_created: [6415,1]//来自TaskWindowContainerController构造方法,
09-19 15:58:37.880  1311  3040 I wm_task_moved: [6415,1,9]//来自TaskWindowContainerController构造方法,具体是在TaskStack中打出的,因为这里调用了stack.addTask
09-19 15:58:37.880  1311  3040 I am_focused_stack: [0,1,0,reuseOrNewTask]//来自setTaskFromReuseOrCreateNewTask最后调用mTargetStack.moveToFront("reuseOrNewTask")
09-19 15:58:37.880  1311  3040 I wm_task_moved: [6415,1,9]//mTargetStack.moveToFront中调用输出
    private int setTaskFromReuseOrCreateNewTask(
            TaskRecord taskToAffiliate, ActivityStack topStack) {
        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);

        // Do no move the target stack to front yet, as we might bail if
        // isLockTaskModeViolation fails below.

        if (mReuseTask == null) {
            final TaskRecord task = mTargetStack.createTaskRecord(
                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
                    mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                    mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                    mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
                    mOptions);
            addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
            updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);

            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
                    + " in new task " + mStartActivity.getTask());
        } else {
            addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
        }

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

        if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }

        if (mDoResume) {
            mTargetStack.moveToFront("reuseOrNewTask");
        }
        return START_SUCCESS;
    }

当Task创建好了之后,我们已经将Task和Activity关联好,并且Task的头部Activity便是我们要启动的Activity,继续执行启动,对应Log如下:

09-19 15:58:37.882  1311  3040 I am_create_task: [0,6415]//打印出如下的Log的时候,此时ams启动Activity已经就该应用Activity创建了属于他的TaskRecord
09-19 15:58:37.882  1311  3040 I am_create_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,android.intent.action.MAIN,NULL,NULL,270532608]
        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);

        mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                mOptions);
        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;
    }

其中mTargetStack.startActivityLocked方法,对应如下Log:

09-19 15:58:37.882  1311  3040 I wm_task_moved: [6415,1,9]//ActivityStack.startActivityLocked 先做一次ActivityStack的移动

当我们创建设置好Task和Stack之后,我们就要真正开始resume启动激活Activity了,继续执行 mSupervisor.resumeFocusedStackTopActivityLocked最终经过层层调用直到ActivityStack的resumeTopActivityInnerLocked:这也是启动过程中第一次调用该方法,由于整个启动过程函数调用相当复杂,这里暂且先预览一下整个执行过程 

miui Launcher进程
Activity.java
startActivity(Intent intent)
  startActivity(intent, null)
    startActivityForResult(intent, -1);
      startActivityForResult(intent, requestCode, null);
        Instrumentation.java
        execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);---Binder IPC--->startActivity

SystemServer进程
ActivityManagerService.java
startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target, requestCode, 0, null, options);
  startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resultWho, requestCode, startFlags, profilerInfo, bOptions,UserHandle.getCallingUserId());
    startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,true /*validateIncomingUser*/);
      ActivityStarter.java
      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)
        startActivity(caller, intent, ephemeralIntent, resolvedType,aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,inTask, allowPendingRemoteAnimationRegistryLookup)
          startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,true /* doResume */, checkedOptions, inTask, outActivity)
            startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, outActivity);
              ActivityStackSupervisor.java
              resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,mOptions);
                ActivityStack.java
                resumeTopActivityUncheckedLocked(target, targetOptions);
                  resumeTopActivityInnerLocked(prev, options);
                    startPausingLocked(userLeaving, false, next, false);
                      ClientLifecycleManager.java
                      scheduleTransaction(prev.app.thread, prev.appToken,PauseActivityItem.obtain(prev.finishing, userLeaving,prev.configChangeFlags, pauseImmediately));
                        ClientTransaction.java
                        schedule();---Binder IPC--->scheduleTransaction

miui Launcher进程
IApplicationThread.java
scheduleTransaction(this);
  ActivityThread.java
    scheduleTransaction(transaction);
      sendMessage(EXECUTE_TRANSACTION)->handleMessage
        TransactionExecutor.java
        execute(transaction);
          executeCallbacks(transaction);
          executeLifecycleState(transaction);
            PauseActivityItem.java
            execute(mTransactionHandler, token, mPendingActions);
              ActivityThread.java
              handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,"PAUSE_ACTIVITY_ITEM");
            postExecute(mTransactionHandler, token, mPendingActions);---Binder IPC--->activityPaused

SystemServer进程
ActivityManagerService.java
activityPaused(token);
  ActivityStack.java
  activityPausedLocked(token, false);
    completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
      ActivityStackSupervisor.java
      resumeFocusedStackTopActivityLocked(topStack, prev, null);
        ActivityStack.java
        resumeTopActivityUncheckedLocked(target, targetOptions);
          ActivityStackSupervisor.java
          startSpecificActivityLocked(next, true, true);
            ActivityManagerService.java
            startProcessLocked(r.processName, r.info.applicationInfo, true, 0,"activity", r.intent.getComponent(), false, false, true,(null != r.launchedFromPackage ? r.launchedFromPackage : "NA"));

QQ进程
一些列调用到ActivityThread.java的main方法,我们称之为应用程序的入口
attach(false, startSeq);
attachApplication(mAppThread, startSeq);---Binder IPC--->attachApplicationLocked
              
SystemServer进程
ActivityManagerService.java
attachApplicationLocked(thread, callingPid, callingUid, startSeq);//am_proc_bound
  bindApplication(processName, appInfo, providers, null, profilerInfo,null, null, null, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.persistent,new Configuration(getGlobalConfiguration()), app.compat,getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, isAutofillCompatEnabled);
  ActivityStackSupervisor.java
  attachApplicationLocked 
    realStartActivityLocked(activity, app,top == activity /* andResume */, true /* checkConfig */)//am_restart_activity
      ClientLifecycleManager.java
        scheduleTransaction(clientTransaction);
          ClientTransaction.java
            schedule();---Binder IPC--->scheduleTransaction

QQ进程
IApplicationThread.java
scheduleTransaction(this);
  ActivityThread.java
    scheduleTransaction(transaction);
      sendMessage(EXECUTE_TRANSACTION)->handleMessage
        TransactionExecutor.java
        execute(transaction);
          executeCallbacks(transaction);
            LaunchActivityItem.java
            execute(mTransactionHandler, token, mPendingActions);
              ActivityThread.java
              handleLaunchActivity(r, pendingActions, null /* customIntent */);
          executeLifecycleState(transaction);
            ResumeActivityItem.java
            execute(mTransactionHandler, token, mPendingActions);
              ActivityThread.java
              handleResumeActivity(token, true /* finalStateRequest */, mIsForward,"RESUME_ACTIVITY");
            postExecute(mTransactionHandler, token, mPendingActions);---Binder IPC--->activityResumed

SystemServer进程
ActivityManagerService.java
activityResumed(token);

第一次调用resumeTopActivityInnerLocked,我们会发现参数prev和next都是指向要启动的QQ的SplashActivity,而当前手机显示的应用是miui launcher,因此我们在启动新的Activity之前需要暂停当前正在显示的应用,因此此时会输出对应的Log:

09-19 15:58:37.885  1311  3040 I am_pause_activity: [3167,192963549,com.miui.home/.launcher.Launcher] //启动一个Activity之前先暂停之前显示的Activity

当miui launcher成功暂停的时候,输出对应Log:

09-19 15:58:37.888  3167  3167 I am_on_paused_called: [0,com.miui.home.launcher.Launcher,handlePauseActivity]

接着会继续第二次执行resumeTopActivityInnerLocked去真正的启动我们的QQ,但是由于QQ首次启动,因此我们先创建其进程,当创建OK好QQ进程之后,会调用QQ进程的ActivityThread的main函数,这样QQ进程便成功启动了,对应Log:

09-19 15:58:37.900  1311  3311 I am_proc_start: [0,31868,10158,com.tencent.mobileqq,activity,com.tencent.mobileqq/.activity.SplashActivity]//来自ActivityManagerService startProcessLocked

但是此时QQ应用进程在系统中的名称并未设置为对应的QQ的进程名,会在bindApplication操作中进行设置。对应Log:

09-19 15:58:37.905  1311  3311 I am_proc_bound: [0,31868,com.tencent.mobileqq]//来自ActivityManagerService  attachApplicationLocked 当系统创建好系统应用进程时,

程创建好之后,继续Activity启动流程,从代码流程中我们可以看出最终是要执行realStartActivityLocked这个方法的,对应会输出Log:

09-19 15:58:37.909  1311  3311 I am_restart_activity: [0,72463824,6415,com.tencent.mobileqq/.activity.SplashActivity,31868]
09-19 15:58:37.909  1311  3311 I am_restart_activity_ai: [31868,com.tencent.mobileqq/.activity.SplashActivity,false]//xmiaomi特有AI启动?黑科技?

realStartActivityLocked方法中会调用stack.minimalResumeActivityLocked(r); 对应会输出Log

09-19 15:58:37.912  1311  3311 I am_set_resumed_activity: [0,com.tencent.mobileqq/.activity.SplashActivity,minimalResumeActivityLocked]

之后会到QQ应用进程去启动首界面SplashActivity,对应从应用程序中会输出Log:

09-19 15:58:38.045 31868 31868 I am_on_resume_called: [0,com.tencent.mobileqq.activity.SplashActivity,LAUNCH_ACTIVITY]//应用进程Log输出

此时应用进程便会开始绘制自己的界面,并与WMS和SurfaceFlinger通信,进行界面展示,至于Activity的界面绘制流程,计划后续会再进行介绍,此处不重点分析。

任务(Task)和堆栈(Stack)-翻译自google网站

任务(Task)是指在执行特定作业时与用户交互的一系列 Activity。 这些 Activity 按照各自的打开顺序排列在堆栈(Stack)中,AMS中分别以TaskRecord和ActivityStack表示,Activity则用ActivityRecord表示。

大多数情况下设备主屏幕即launcher界面,就是任务的起点,当用户触摸应用启动器中的图标(或主屏幕上的快捷方式)时,该应用的任务将出现在前台。 如果应用不存在任务(应用最近未曾使用),则会创建一个新任务,并且该应用的“主”Activity 将作为堆栈中的根 Activity 打开。

当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。 前一个 Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。 用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。 堆栈中的 Activity 永远不会重新排列,仅推入和弹出堆栈:由当前 Activity 启动时推入堆栈;用户使用“返回”按钮退出时弹出堆栈。 因此,返回栈以“后进先出”对象结构运行。

下图 通过时间线显示 Activity 之间的进度以及每个时间点的当前返回栈,直观呈现了这种行为。

显示任务中的每个新 Activity 如何向返回栈添加项目。 用户按“返回”按钮时,当前 Activity 随即被销毁,而前一个 Activity 恢复执行。

如果用户继续按“返回”,堆栈中的相应 Activity 就会弹出,以显示前一个 Activity,直到用户返回主屏幕为止,当所有 Activity 均从堆栈中移除后,任务即不复存在。

任务是从用户角度来管理Activity的,当用户开始新的任务或者按home键回到主屏幕时,可以移到到后台,当该任务到后台时,任务中所有的Activity全部处于停止状态,但是任务的堆栈顺序是不会改变的,从window的角度来说,该任务只是失去焦点而已,

两个任务:任务 B 在前台接收用户交互,而任务 A 则在后台等待恢复。

当该任务获得焦点时,用户即可回到离开时的状态。但是如果后台运行着多个任务,系统为了保持流畅性,可能会开始销毁后台的Activity,以回收内存资源,从而导致Activity的状态丢失,详细请参见Activity状态

正常情况下,由于栈中的 Activity 永远不会重新排列,因此如果应用允许用户从多个 Activity 中启动特定 Activity,则会创建该 Activity 的新实例并推入堆栈中(而不是将 Activity 的任一先前实例置于顶部),因此,应用中的一个 Activity 可能会多次实例化(即使 Activity 来自不同的任务)如下图

但是,如果我们不希望 Activity 多次实例化,则可修改此行为,详细请参见Task管理。系统中Activity和Task的默认行为如下:

  • Activity A 启动 Activity B 时,Activity A 将会停止,但系统会保留其状态(例如,滚动位置和已输入表单中的文本)。如果用户在处于 Activity B 时按“返回”按钮,则 Activity A 将恢复其状态,继续执行。

  • 用户通过按“主页”按钮离开任务时,当前 Activity 将停止且其任务会进入后台。 系统将保留任务中每个 Activity 的状态。如果用户稍后通过选择开始任务的启动器图标来恢复任务,则任务将出现在前台并恢复执行堆栈顶部的 Activity。

  • 如果用户按“返回”按钮,则当前 Activity 会从堆栈弹出并被销毁。 堆栈中的前一个 Activity 恢复执行。销毁 Activity 时,系统不会保留该 Activity 的状态。

  • 即使来自其他任务,Activity 也可以多次实例化。

写在最后 

刚写本篇之初,以”ActivityManagerService之Activity启动揭秘“为题,斟酌一下,还是以”ActivityManagerService之Activity启动初探“,本篇内容更多的接近于介绍Activity初次启动的流程,部分涉及AMS中Stack和Task的管理,并未详细介绍其中的细节,因此计划后续将AMS中各个细节部分,深入剖析一下。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/abm1993/article/details/82773652

智能推荐

Matlab添加工具箱对相机+投影仪系统进行标定_projector-camera calibration toolbox matlab-程序员宅基地

文章浏览阅读3.3k次,点赞4次,收藏64次。相机+投影仪系统标定写在前面1.写的比较详细,所以拆分成几部分。2.使用Matlab添加额外的工具箱完成标定。3.使用棋盘格标定相机、投影仪。标定工具下载地址1.相机标定工具箱Bouguet’s Calibration Toolbox ,详细使用说明见Camera Calibration Toolbox for Matlab2.投影仪标定工具箱,详细使用说明见论文。..._projector-camera calibration toolbox matlab

不是内部或外部命令,也不是可运行的程序或批处理文件_ascp' 不是内部或外部命令,也不是可运行的程序 或批处理文件。-程序员宅基地

文章浏览阅读4.8k次。win7用win + R,输入cmd打开命令操作符输入指令时,常会出现如题的问题。解决方法如下:此时路径是:C:\Users\pccd C:\Windows\system32 // 改变路径到C:\Windows\system32发现再输入指令,系统就能识别了。..._ascp' 不是内部或外部命令,也不是可运行的程序 或批处理文件。

HTML聊天框特效,利用jQuery实现响应式聊天窗口界面特效-程序员宅基地

文章浏览阅读374次。特效描述:利用jQuery实现 响应式 聊天窗口 界面特效。利用jQuery实现响应式聊天窗口界面特效代码结构1. 引入CSS2. 引入JS3. HTML代码10Conversations关闭返回 这是用户的名字,看看名字到底能有多长关闭自酌一杯酒10白兰地8威士忌2荷兰金酒20朗姆酒10特其拉酒18鸡尾酒9虎骨酒12琴酒99+葡萄酒蓝莓酒樱花酒201..._html jquery 聊天窗口

Login.java_login.java程序-程序员宅基地

文章浏览阅读577次。 package yaopin;import javax.swing.ImageIcon;import javax.swing.JOptionPane;import javax.swing.JPanel;import javax.swing.JFrame;import javax.swing.JLabel;import java.awt.Rectangle;import java.awt.ev_login.java程序

ASA与PIX的区别-程序员宅基地

文章浏览阅读68次。很多年来,Cisco PIX一直都是Cisco确定的防火墙。但是在2005年5月,Cisco推出了一个新的产品——适应性安全产品(ASA,Adaptive Security Appliance)。不过,PIX还依旧可用。我已听到很多人在多次询问这两个产品线之间的差异到底是什么。让我们来看一看。Cisco PIX是什么?Cisco PIX是一种专用的硬件防火墙。所有版本..._pix asa区别

TensorFlow conv2d原理及实践-程序员宅基地

文章浏览阅读235次。tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)官方教程说明:给定四维的input和filtertensor,计算一个二维卷积Args:input: ATensor. type必须是以下几种类型之一:half,float32,..._conv2d原理 l

随便推点

多元分类预测 | Matlab 深度置信网络(DBN)分类预测_dbn训练过程准确率显示 matlab-程序员宅基地

文章浏览阅读108次。在电力系统中,变压器是一个至关重要的组件,用于将电能从一个电路传输到另一个电路,以满足不同电压等级的需求。然而,由于长期使用和环境因素的影响,变压器可能会出现各种故障,这可能导致电力系统的不稳定和损坏。因此,准确和及时地诊断变压器故障对于确保电力系统的可靠性和安全性至关重要。近年来,深度学习技术已经在各个领域取得了巨大的成功,包括图像识别、语音识别和自然语言处理等。在电力系统领域,深度学习技术也被广泛应用于变压器故障诊断。_dbn训练过程准确率显示 matlab

自媒体矩阵运营计划书:成功策划秘籍揭秘_自媒体矩阵运营商业计划书-程序员宅基地

文章浏览阅读286次,点赞5次,收藏10次。在当下社会环境下,一份完满的自媒体矩阵运营计划书对于自媒体从业人员至关重要;它不仅能够辅助自媒体工作者进行运营策略的制定与执行,更能提供精准的数据分析及决策支持。以下内容,即为详述如何策划打造一份成功的自媒体矩阵运营计划书,以助您在竞争中立于不败之地。一、明确目标与定位拟定自媒体矩阵运营规划前_自媒体矩阵运营商业计划书

lisp 正则表达式示例-程序员宅基地

文章浏览阅读879次。lisp正则表达式可以用多个第三方的包,cliki推荐是cl-ppcre这个包.在代码中写正则表达式可以用cl-interpol这个包方便转义cl-interpol如果要匹配一对括号不用cl-interpol,需要写成"\\(\\)"借助cl-interpol只需下面的写法CL-USER> #?R"\(\)""\\(\\)"CL-US..._lisp正则表达式

ThinkPHP学习(四)volist标签高级应用之多重嵌套循环、隔行变色_thinkphp volist 单双行-程序员宅基地

文章浏览阅读2.4w次。Action代码: public function index(){ $prod = I("get.prod_en"); $id = I("get.id", 0, "int"); if ($prod == ""){ $serviceProduct = array();//多重循环遍历的数组 //数据保存在两张表中,这里通过循环初始化$serviceProduct数组_thinkphp volist 单双行

C语言多线程编程之一_c语言 线程 传参-程序员宅基地

文章浏览阅读533次,点赞2次,收藏2次。在C语言中可以使用pthread.h的API来创建线程,pthread.h符合POSIX标准,意味可以在Unix和Linux下运行,WindowsNT也提供了相应的支持。_c语言 线程 传参

linux服务器搭建实验4报告,LINUX实验四报告-程序员宅基地

文章浏览阅读678次。《LINUX实验四报告》由会员分享,可在线阅读,更多相关《LINUX实验四报告(6页珍藏版)》请在人人文库网上搜索。1、一、 实验目的掌握Linux系统中常用命令的作用和命令中各选项的作用。二、实验环境安装了fedora17系统的计算机一台三、实验过程(实验步骤、记录、数据、分析、结果)1 查看当前系统下用户Shell定义的环境变量的值。2 定义变量AK的值为200,并将其显示在频幕上。3 定义变..._创建一个简单的shell程序,其功能为显示计算机主机名以及显示系统日期和时间。