Android 7.0 ActivityManagerService 启动Activity的过程:一

Posted ZhangJianIsAStark

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 7.0 ActivityManagerService 启动Activity的过程:一相关的知识,希望对你有一定的参考价值。

从这一篇博客开始,我们将阅读AMS启动一个Activity的代码流程。
自己对Activity的启动过程也不是很了解,这里就初步做一个代码阅读笔记,为以后的迭代打下一个基础。

一、基础知识
在分析Activity的启动过程前,有必要先了解一下Activity相关的基础知识。

1、Task和Activity的设计理念
关于android中Task和Activity的介绍,个人觉得《深入理解Android》中的例子不错。
我们就借鉴其中的例子,进行相应的说明:

上图列出了用户在Android系统上想干的三件事,分别用A、B、C表示。
在Android中,每一件事可以被看作是一个Task;一个Task可以被细分成多个子步骤,每个子步骤可以被看作是一个Activity。
从上图可以看出,A、B两个Task使用了不同的Activity来完成相应的任务,即A、B两个Task的Activity之间没有复用。
但是在Task C中,分别使用了Task A中的A1、Task B中的B2。

这么设计的原因是:用户想做的事情(Task)即使完全不同,但是当细分Task为Activity时,就可能出现Activity功能类似的情况。
当Task A和Task B中已经有能满足需求的Activity时,Task C就会优先复用而不是重新创建Activity。
通过重用Activity可以节省一定的开销,同时为用户提供一致的界面和用户体验。

对Android的设计理念有一定的了解后,我们看看Android是如何组织Task及它所包含的Activity。

上图为一个比较经典的示例:图中的Task包含4个Activity。用户可以单击按钮跳转到下一个Activity。同时,通过返回键可以回到上一个Activity。
图中虚线下方为Activity的组织方式。从图中可以看出,Android是以Stack的方式来管理Activity的。
先启动的Activity成为栈底成员,被启动的Activity将作为栈顶成员显示在界面上。
当按返回键时,栈顶成员出栈,前一个Activity成为栈顶显示在界面上。

以上是一个Task的情况。当有多个Task时,Android系统只支持一个处于前台的Task,其余的Task均处于后台。
这些后台Task内部Activity保持顺序不变。用户可以一次将整个Task挪到后台或置为前台,如下图所示:

在AMS中,将用ActivityRecord来作为Activity的记录者、TaskRecord作为Task的记录者,TaskRecord中有对应的ActivityStack专门管理ActivityRecord。

2、启动模式
Android定义了4种Activity的启动模式,分别为Standard、SingleTop、SingleTask和SingleInstance。

Standard模式
我们平时直接创建的Activity都是这种模式。
这种模式的Activity的特点是:只要你创建并启动了Activity实例,Android就会向当前的任务栈中加入新创建的实例。退出该Activity时,Android就会在任务栈中销毁该实例。
因此,一个Task中可以有多个相同类型的Activity(类型相同,但不是同一个对象)。

Standard模式启动Activity的栈结构如下图所示:

SingleTop模式
这种模式会考虑当前要激活的Activity实例在任务栈中是否正处于栈顶。
如果处于栈顶则无需重新创建新的实例,将重用已存在的实例,
否则会在任务栈中创建新的实例。

SingleTop模式启动Activity的栈结构如下图所示:

注意:当用SingleTop模式启动位于栈顶的Activity时,并不会创建新的Activity,但栈顶Activity的onNewIntent函数将被调用。

SingleTask模式
在该种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例。和SingleTop一样,系统也会回调其onNewIntent。

具体一点,当一个具有singleTask模式的Activity A请求启动后,系统先会寻找是否存在A想要的任务栈。
如果不存在对应任务栈,就重新创建一个任务栈,然后创建A的实例后,把A放到任务栈中。
如果存在A所需的任务栈,那么系统将判断该任务栈中是否有实例A。
如果有实例A,那么系统就将A调到栈顶并调用其onNewIntent方法(会清空A之上的Activity)。
如果没有实例A,那么系统就创建实例A并压入栈中。

SingleTask模式启动Activity的栈结构如下图所示:

SingleInstance模式
SingleInstance模式是一种加强版的SingleTask模式,它除了具有SingleTask所有的特性外,还加强了一点,那就是具有此模式的Activity只能单独地位于一个任务栈中。

3、Intent Flags
启动模式主要是配置在xml文件中的,例如:

<activity android:name=".TestActivity"
    android:launchMode="singleTask"
>

除了启动模式外,Android在用Intent拉起Activity时,还可以使用Intent Flags控制Activity及Task之间的关系。
Intent Flags数量非常多,这里只列举其中的一部分:

Intent.FLAG_ACTIVITY_NEW_TASK
默认的跳转类型,将目标Activity放到一个新的Task中。

Intent.FLAG_ACTIVITY_CLEAR_TASK
当用这个FLAG启动一个Activity时,系统会先把与该Activity有关联的Task释放掉,然后启动一个新的Task,并把目标Activity放到新的Task。
该标志必须和Intent.FLAG_ACTIVITY_NEW_TASK一起使用。

FLAG_ACTIVITY_SINGLE_TOP
这个FLAG就相当于启动模式中的singleTop。
例如:原来栈中结构是A B C D。现在,在D中启动D,那么栈中的结构还是A B C D。

FLAG_ACTIVITY_CLEAR_TOP
这个FLAG类似于启动模式中的SingleTask。
这种FLAG启动的Activity会其之上的Activity全部弹出栈空间。
例如:原来栈中的结构是A B C D ,从D中跳转到B,栈中的结构就变为了A B了。

FLAG_ACTIVITY_NO_HISTORY
用这个FLAG启动的Activity,一旦退出,就不会存在于栈中。
例如:原来栈中的结构是A B C,现在用这个FLAG启动D。然后在D中启动E,栈中的结构为A B C E。

对这些基础知识有了一定的了解后,我们来看看AMS启动Activity的代码级流程。

在这一篇博客中,我们对代码流程的分析,将截止于启动Activity对应的进程。
于是,这部分流程中大部分的内容,将围绕Activity如何选择对应的Task来展开,
由于Task的选择还要涉及对启动模式、Intent Flags等的判断,
因此整个代码将极其的琐碎,需要很有耐心才能较仔细地看完。

二、am命令
我们将看看利用am命令如何启动一个Activity。

之所以选择从am命令入手,是因为当我们从一个Activity拉起另一个Activity时,
当前Activity对应的进程需要和AMS进行交互,
这就要求我们需要对进程中与AMS交互的对象比较了解时,才比较容易分析。

而从am入手分析,当被启动Activity被创建后,代码流程自然就会涉及到这个进程与AMS的交互,
整个逻辑的顺序很容易理解。

当我们利用adb shell进入到手机的控制台后,可以利用am命令启动Activity、Service等。
具体的格式类似于:

am start -W -n 包名(package)/包名.activity名称

例如,启动浏览器的命令是:

am start -W -n com.android.browser/com.android.browser.BrowserActivity

上面命令中的-W是一个可选项,表示等待目标activity启动后,am才返回结果;
-n ,表示后接COMPONENT。
am命令可接的参数有很多种,有兴趣可以研究一下,此处不再一一列举。

如同之前介绍pm安装apk的流程中提及的,pm命令是一个执行脚本。
am与pm一样,同样是定义于手机中的执行脚本。
am脚本的文件路径是frameworks/base/cms/am,其内容如下:

#!/system/bin/sh
#
# Script to start "am" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "$@"

与调用pm命令类似,调用am命令同样最终会调用到Am.java(frameworks/base/cmds/am/src/com/android/commands/am)的main函数。
这里的调用过程可以参考Android7.0 PackageManagerService (3) APK安装的第二部分。

现在我们直接看看Am.java的main函数:

/**
* Command-line entry point.
*
* @param args The command-line arguments
*/
public static void main(String[] args) {
    //创建一个Am对象,然后执行run函数
    (new Am()).run(args);
}

Am继承自BaseCommand,上面的run函数定义于BaseCommand中:

/**
* Call to run the command.
*/
public void run(String[] args) {
    ..........
    //将字符串封装到对象中,mArgs的类型为ShellCommand
    mArgs.init(null, null, null, null, args, 0);
    ..........
    try {
        //子类实现
        onRun();
    } catch (IllegalArgumentException e) {
        ......
    } catch (Exception e) {
        ......
    }
}

现在进入到Am.java的onRun函数:

public void onRun() throws Exception {
    mAm = ActivityManagerNative.getDefault();
    ..............
    mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    ...............
    //从父类的mArgs中中取出第一个参数
    String op = nextArgRequired();

    if (op.equals("start")) {
        runStart();
    } else if (op.equals("startservice")) {
        runStartService();
    } .........
    ...........
}

从代码可以看出,am命令的功能很多,此处我们主要看看start相关的runStart函数:

private void runStart() throws Exception {
    //makeIntent会解析参数,得到对应的Intent
    //主要是结合Intent的parseCommandArgs函数和Am内部定义的CommandOptionHandler解析字符串
    //比较简单,不做深入分析
    Intent intent = makeIntent(UserHandle.USER_CURRENT);
    ..........
    //获取mimeType
    String mimeType = intent.getType();
    if (mimeType == null && intent.getData() != null
            && "content".equals(intent.getData().getScheme())) {
        //如果是"content"类型的数据,那么利用AMS获取对应的mimeType
        mimeType = mAm.getProviderMimeType(intent.getData(), mUserId);
    }
    ..........
    do {
        if (mStopOption) {
            //处理-S选项,即先停止对应的Activity,再启动它
            //这些变量,均是makeIntent函数解析参数得到的
            ...............
        }
        ............
        //通过am命令启动的Activity,附加了标志FLAG_ACTIVITY_NEW_TASK
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        ............
        if (mProfileFile != null) {
            //处理-P选项,用于性能统计
            .............
        }
        .............
        //通过添加参数--stack,可以指定Activity加入到特定的Task中
        //此处就是将对应的Stack Id将被写入到options中
        //与脚本命令中的 -W 一样,--stack是一个可选项
        ActivityOptions options = null;
        if (mStackId != INVALID_STACK_ID) {
            options = ActivityOptions.makeBasic();
            options.setLaunchStackId(mStackId);
        }

        if (mWaitOption) {
            //如果有-W选项,进入该分支
            result = mAm.startActivityAndWait(null, null, intent, mimeType,
                    null, null, 0, mStartFlags, profilerInfo,
                    options != null ? options.toBundle() : null, mUserId);
            res = result.result;
        } else {
            //不等待activity启动,直接返回
            res = mAm.startActivityAsUser(null, null, intent, mimeType,
                    null, null, 0, mStartFlags, profilerInfo,
                    options != null ? options.toBundle() : null, mUserId);
        }
        //判断am命令是否执行成功,成功时会break
        ..........
        mRepeat--;
        .........
    }while (mRepeat > 1);
}

从上面的代码可以看出,am最终将调用AMS的startActivityAndWait或startActivityAsUser函数,来启动参数指定的Activity。
我们以startActivityAndWait为例进行分析。

三、startActivityAndWait流程
startActivityAndWait的参数比较多,先来大致看一下参数的含义:

public final WaitResult startActivityAndWait(
//在多数情况下,一个Activity的启动是由一个应用进程发起的
//IApplicationThread是应用进程和AMS交互的通道
//通过am启动Activity时,该参数为null
IApplicationThread caller, 

//应用进程对应的pacakge
String callingPackage,

//启动使用的Intent和resolvedType
Intent intent, String resolvedType, 

//均是给Activity.java中定义的startActivityForResult使用的
//resultTo用于接收返回的结果,resultWho用于描述接收结果的对象
//requestCode由调用者定义
IBinder resultTo, String resultWho, int requestCode,

//Intent携带的start activity对应的flag
int startFlags,

//性能统计有关
ProfilerInfo profilerInfo, 

//用于指定Activity的一些选项
//从前面调用的代码来看,应该是指定Activity需要加入的Task
Bundle bOptions,

//表示调用的用户ID
int userId) {
    ..................
}

现在我们看看startActivityAndWait函数的具体内容:

public final WaitResult startActivityAndWait(....) {
    //进行权限检查相关的工作
    .............

    //用于存储处理结果
    WaitResult res = new WaitResult();

    //进入ActivityStarter中的流程
    mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
            null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,
            bOptions, false, userId, null, null);

    return res;
}

上面代码中的ActivityStarter初始化于AMS的构造函数中,专门负载启动Activity相关的工作。
当我们通过am命令启动一个Activity时,假设系统之前没有启动过该Activity,那么从功能的角度来看,ActivityStarter调用artActivityMayWait函数后,系统将完成以下工作:
1、上文提及在Am.java中,为Intent增加了标志位FLAG_ACTIVITY_NEW_TASK,因此系统将为Activity创建ActivityRecord和对应的TaskRecord。
2、系统需要启动一个新的应用进程以加载并运行该Activity。
3、还需要停止当前正在显示的Activity。

接下来,我们跟进一下ActivityStarter的startActivityMayWait函数。
我们可以将该函数分为三部分进行分析:

1 第一部分

final int startActivityMayWait(............) {
    ...............
    //判断是否指定了组件名
    boolean componentSpecified = intent.getComponent() != null;
    ...............

    //利用PKMS解析满足Intent等参数要求的信息
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
    ...............

    // Collect information about the target of the Intent.
    // mSupervisor的类型为ActivityStackSupervisor, 负责管理Activity和对应Task之间的关系
    // 此处,ActivityStackSupervisor实际仅从ResolveInfo中取出对应的ActivityInfo
    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);

    //得到options,其中可能指定了Activity需要加入的Task
    ActivityOptions options = ActivityOptions.fromBundle(bOptions);

    ActivityStackSupervisor.ActivityContainer container =
            (ActivityStackSupervisor.ActivityContainer)iContainer;
    synchronized (mService) {
        //从am启动时,container为null
        if (container != null && container.mParentActivity != null &&
                container.mParentActivity.state != RESUMED) {
            // Cannot start a child activity if the parent is not resumed.

            //如果从一个Activity启动另一个Activity,从此处代码可以看出,
            //要求父Activity已经执行过onResume
            return ActivityManager.START_CANCELED;
        }

        final int realCallingPid = Binder.getCallingPid();
        final int realCallingUid = Binder.getCallingUid();
        ....................

        //以下代码是决定启动Activity时的Task
        final ActivityStack stack;
        if (container == null || container.mStack.isOnHomeDisplay()) {
            //am启动,或Home来启动Activity
            //stack为前台栈
            stack = mSupervisor.mFocusedStack;
        } else {
            //当从一个Activity启动另一个Activity时,
            //启动栈为父Activity的Task
            stack = container.mStack;
        }

        //am启动时config == null
        stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
        .................

        //正常情况下,当一个Application退到后台时,系统会为它保存状态;当调度其到前台时,恢复它之前的状态,以保证用户体验的连续性
        //AndroidManifest.xml中的Application标签可以申明一个CANT_SAVE_STATE属性
        //设置了该属性的Application将不享受系统提供的状态保存/恢复功能,被称为heavy-weight process
        if (aInfo != null &&
                (aInfo.applicationInfo.privateFlags
                        & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
            ............................
        }
        ...................
    }
}

从上面的代码来看,startActivityMayWait在第一阶段最主要的工作其实就是:
1、解析出与Intent相匹配的ActivityInfo。
2、得到启动该Activity的Task,即父Activity的Task或前台Task。

2 第二部分

..................
//用于保存启动Activity后,对应的ActivityRecord
final ActivityRecord[] outRecord = new ActivityRecord[1];

//调用startActivityLocked函数,进行实际的启动工作
int res = startActivityLocked(...............);
..................

这一部分中,涉及到了启动Activity的核心函数startActivityLocked。该函数比较复杂,我们在后面单独分析。
当该函数成功执行完毕后,Activity将会被启动,并形成对应的ActivityRecord被AMS统一管理。

我们先看看startActivityMayWait函数第三部分的工作。

3 第三部分

...................
//outResult不等于null,表示等待启动结果
//目标Activity要运行在一个新的应用进程中,因此需要等待应用进程正常启动并处理相关请求
if (outResult != null) {
    outResult.result = res;
    if (res == ActivityManager.START_SUCCESS) {
        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;
        }
    }

    if (res == START_TASK_TO_FRONT) {
        //Activity对应的task拉到前台后,一直要等到该界面被加载
        ActivityRecord r = stack.topRunningActivityLocked();
        if (r.nowVisible && r.state == RESUMED) {
            outResult.timeout = false;
            outResult.who = new ComponentName(r.info.packageName, r.info.name);
            outResult.totalTime = 0;
            outResult.thisTime = 0;
        } else {
            outResult.thisTime = SystemClock.uptimeMillis();
            mSupervisor.mWaitingActivityVisible.add(outResult);
            do {
                try {
                    mService.wait();
                } catch (InterruptedException e) {
                }
            } while (!outResult.timeout && outResult.who == null);
        }
    }
}
...............

从上面的代码可以看出,第三阶段的工作就是根据返回值做一些处理。
由于我们在输入的命令时,指定了-W选项,因此将进入wait状态等待Activity界面被显示。

四、startActivityLocked流程
接下来,我们看看上面提及到的核心函数startActivityLocked:

final int startActivityLocked(..............) {
    //err用于保存错误信息
    int err = ActivityManager.START_SUCCESS;

    //用于保存启动Activity对应的进程信息
    ProcessRecord callerApp = null;

    //如果参数中的调用者不为空,则从AMS中找到对应的ProcessRecord,目的是得到调用者的pid和uid
    //当利用am命令启动时,caller等于null
    if (caller != null) {
        callerApp = mService.getRecordForAppLocked(caller);
        if (callerApp != null) {
            callingPid = callerApp.pid;
            callingUid = callerApp.info.uid;
        } else {
            ................
        } 
    }

    final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
    .........................
    //sourceRecord用于保存父Activity的信息
    ActivityRecord sourceRecord = null;

    //resultRecord用于保存接收启动结果的Activity
    ActivityRecord resultRecord = null;

    //对于startActivityForResult才有意义
    if (resultTo != null) {
        //利用ActivityStackSupervisor判断是否有resultTo对应的ActivityRecord
        //这里的隐含条件是,resultTo的对象就是父Activity
        sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
        .................
        if (sourceRecord != null) {
            if (requestCode >= 0 && !sourceRecord.finishing) {
                resultRecord = sourceRecord;
            }
        }
    }

    //得到启动Activity使用的标志位
    final int launchFlags = intent.getFlags();

    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.
        //以这个标签启动的Activity,将接收原本发往父Activity的result
        //这部分代码没细看,感觉没什么用吧。。。
        ....................
    }

    //检查一些条件是否满足,修改err的状态
    .....................

    //得到接收启动结果的Task
    final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;

    if (err != START_SUCCESS) {
        if (resultRecord != null) {
            //如果存在err,需要返回错误信息
            resultStack.sendActivityResultLocked(
                    -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
        }
        ActivityOptions.abort(options);
        return err;
    }

    //检查权限
    boolean abort = !mSupervisor.checkStartAnyActivityPermission(.............);

    //根据IntentFirewall判断Intent是否满足要求
    abort |= !mService.mIntentFirewall.checkStartActivity(.............);

    //通过接口,可以为AMS设置一个IActivityController类型的监听者;AMS进行操作时,将会回调该监听者
    //例如进行Monkey测试的时候,Monkey会设置该回调对象
    if (mService.mController != null) {
        try {
            Intent watchIntent = intent.cloneFilter();

            //交给回调对象处理,判断能否进行后续流程
            //进行Monkey测试时,可以设置黑名单,处于黑名单中的Activity将不能启动
            abort |= !mService.mController.activityStarting(watchIntent,
                    aInfo.applicationInfo.packageName);
        } catch (RemoteException e) {
            mService.mController = null;
        }
    }
    ................
    //以上任一条件不满足时,进行通知
    if (abort) {
        if (resultRecord != null) {
            resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
                    RESULT_CANCELED, null);
        }
        // We pretend to the caller that it was really started, but
        // they will just get a cancel result.
        ActivityOptions.abort(options);
        return START_SUCCESS;
    }

    // If permissions need a review before any of the app components can run, we
    // launch the review activity and pass a pending intent to start the activity
    // we are to launching now after the review is completed.
    //在必要时,再检查一下权限,代码未细看,暂时觉得没有必要看
    if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) {
        ................
    }
    ..............
    //创建一个ActivityRecord对象
    ActivityRecord r = new ActivityRecord(.........);
    if (outActivity != null) {
        outActivity[0] = r;
    }
    .........................
    final ActivityStack stack = mSupervisor.mFocusedStack;
    if (voiceSession == null && (stack.mResumedActivity == null
            || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
        //检查调用进程是否有权限切换Activity
        if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                realCallingPid, realCallingUid, "Activity start")) {

            //如果调用进程没有权限进行切换,则将本次Activity的启动请求保存起来
            //后续有机会再进行启动
            PendingActivityLaunch pal =  new PendingActivityLaunch(r,
                    sourceRecord, startFlags, stack, callerApp);
            mPendingActivityLaunches.add(pal);
            ActivityOptions.abort(options);
            return ActivityManager.START_SWITCHES_CANCELED;
        }
    }

    //用于控制app switch
    if (mService.mDidAppSwitch) {
        mService.mAppSwitchesAllowedTime = 0;
    } else {
        mService.mDidAppSwitch = true;
    }

    //启动处于pending状态的Activity
    doPendingActivityLaunchesLocked(false);

    try {
        //WindowManager延迟绘制
        //个人觉得可能是为了优化性能,比如当前界面还有细节未绘制完,但要拉起一个新的界面,那么此时就不需要绘制了)
        mService.mWindowManager.deferSurfaceLayout();

        //调用startActivityUnchecked
        err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true, options, inTask);
    } finally {
        //WindowManager重新开始绘制(绘制当前的前台界面)
        mService.mWindowManager.continueSurfaceLayout();
    }
    //此处将通知ActivityStarter, Activity对应的Task被移动到前台
    postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);
    return err;
}

startActivityLocked函数比较长,但主干比较清晰,只是添加许多条件判断。

从代码来看主要工作包括:
1、处理sourceRecord和resultRecord。
sourceRecord表示发起本次请求的Activity,即父Activity对应的信息;
resultRecord表示接收处理结果的Activity。
在一般情况下,sourceRecord和resultRecord应指向同一个Activity。

2、处理app switch。
如果AMS当前禁止app switch,那么AMS会将本次请求保存起来,以待允许app switch时再进行处理。

从代码可以看出,当AMS可以进行app switch时,在处理本次的请求前,会先调用doPendingActivityLaunchesLocked函数。
doPendingActivityLaunchesLocked函数将启动之前因系统禁止app switch而保存的请求。

3、调用startActivityUnchecked处理本次Activity的启动请求。

在分析接下来的流程前,我们先看看app switch相关的内容。
在AMS中,提供了两个函数stopAppSwitches和resumeAppSwitches,用于暂时禁止App切换及恢复切换。
这种需求的考虑是:当某些重要的Activity处于前台时,不希望系统因为用户操作之外的原因切换Activity。

1、stopAppSwitches
先来看看stopAppSwitches:

public void stopAppSwitches() {
    //检查调用进程是否有STOP_APP_SWITCHES权限
    ..............

    synchronized(this) {
        //设置了一个超时时间,目前为5s
        //过了该时间,AMS可以重新切换App
        mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
                + APP_SWITCH_DELAY_TIME;
        mDidAppSwitch = false;

        //发送一个延迟消息,触发允许App Switch的操作
        mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
        Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
        mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);
    }
}

对于上面的代码,需要注意两点:
1、此处的控制机制名为app switch,而不是Activity switch。
这是因为如果从受保护的Activity中启动另一个Activity,那么这个新的Activity的目的应该是针对同一个任务。
于是这次的启动就不应该受app switch的制约。

2、执行stopAppSwitches后,应用程序应该调用resumeAppSwitches以允许app switch。
为了防止应用程序有意或者无意没调用resumeAppSwitches,在stopAppSwitches中设置了一个超时时间,过了此超时时间,系统会发送一个消息触发App Switch的操作。

2、resumeAppSwitches
现在我们看看resumeAppSwitches的代码:

public void resumeAppSwitches() {
    //同样是进行权限检查
    .............

    synchronized(this) {
        // Note that we don't execute any pending app switches... we will
        // let those wait until either the timeout, or the next start
        // activity request.
        mAppSwitchesAllowedTime = 0;
    }
}

从代码可以看出,resumeAppSwitches只设置了mAppSwitchesAllowedTime的值为0,它并不处理在stop和resume这段时间内积攒起的Pending请求。
根据前面startActivityLocked函数,我们知道如果在执行resume app switch后,又有新的请求需要处理,则先调用doPendingActivityLaunchesLocked处理那些pending的请求。
此外,resumeAppSwitches函数中并没有撤销stopAppSwitches函数中设置的超时消息,所以当该消息被处理时,同样会触发处理pending请求的流程。

五、startActivityUnchecked流程
顺着请求的处理流程,我们接下来看看startActivityUnchecked函数。
startActivityUnchecked函数比较长,我们分段看一下。

Part-I

第一部分如下代码所示,主要用于判断是否需要为新的Activity创建一个Task。

private int startActivityUnchecked(.......) {
    //根据参数重新设置类的成员变量
    //将存储当前Activity对应的启动模式等信息
    setInitialState(............);

    computeLaunchingTaskFlags();

    computeSourceStack();

    mIntent.setFlags(mLaunchFlags);
...................

我们依次看看上述代码中的几个函数:
1、setInitialState

private void setInitialState(.........) {
    //重置当前类的成员变量
    reset();

    //用于保存当前准备启动的Activity
    mStartActivity = r;

    mIntent = r.intent;
    ...........
    mSourceRecord = sourceRecord;
    ...........

    mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
    mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
    mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
    ................

    // We'll invoke onUserLeaving before onPause only if the launching
    // activity did not explicitly state that this is an automated launch.
    //判断是否需要调用因本次Activity启动,而被系统移到后台的当前Activity的onUserLeaveHint函数
    mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0;
    ................

    if (mOptions != null && mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
        r.mTaskOverlay = true;
        final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
        final ActivityRecord top = task != null ? task.getTopActivity() : null;
        if (top != null && !top.visible) {

            // The caller specifies that we'd like to be avoided to be moved to the front, so be
            // it!
            Android 7.0下仿7.0之前的时间框及去掉日选择框方法

Android 7.0下仿7.0之前的时间框及去掉日选择框方法

关于 Android 7.0 适配中 FileProvider 部分的总结

Android 7.0 抓包配置

Android编程入门--android 7.0 的so文件

Android 7.0动态权限大总结