AMS- Activity分析

Activity的管理算是ams中最为复杂的部分,为了弄清楚WMS的窗口管理,不可能跳过Activity的管理,这里就进行第三遍系统的分析Activity的管理

AMS—->
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions)
   ——>
    startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId())
1检查不是沙盒进程调用,否则抛出异常
2 获取userId
int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
int allowMode, String name, String callerPackage)
由于我们对多用户不太了解,可以通过阅读的过程对其脉络进行分析
1 从callingUid获取到callingUserId(调用者的userId),如果callingUserId == userId,说明当前调用者的和要求以某个user(叫做targetUser)启动的这个targetUser是同一个user,所以asUser其实就是as自己拉,不用做多余的检查
2把targetUser 转换为user,这个怎么解释呢,因为有两个特殊的userId, UserHandle.USER_CURRENT和UserHandle.USER_CURRENT_OR_SELF 要转乘当前的登录user,这很好理解,所以不去详细说明
3 对于其他user的情况要进行一系列验证是否可以进行组件的启动,验证组件是否可以在callingUser下启动还要满足当前的不是SYSTEM_UID并且callingUid=0,如果callingUid==0说明是root权限的进程,而callingUid == SYSTEM_UID则说明调用者是系统进程,这二者都是有绝对特权的,不需要进行检验.这不仅让我们产生了一个猜想,我们自己写一个异步的binder调用去启动服务,获取到的callingUid和callingPid都是0,是不是能启动任何页面了呢?
检查的过程分为如下几种情况
1检查INTERACT_ACROSS_USERS_FULL权限,如果有这个权限则允许启动组件
2 如果没有INTERACT_ACROSS_USERS_FULL要求只检查ALLOW_FULL 则不满足不能启动组件
3 如果上述两个条件都不满足,就检查INTERACT_ACROSS_USERS权限, 不满足权限不允许启动,满足了还要做后续检查
4 不是只检查ALLOW_NON_FULL则3中权限满足则允许启动
5 如果3权限通过,而检查ALLOW_NON_FULL_IN_PROFILE就要调用isSameProfileGroup(callingUserId, targetUserId)检查calling和target要在同一个受限用户组里才能启动
6 上述清空都不满足抛出异常
4 如果不允许启动,但是targetUserId为则设置targetUserId = callingUserId,从这里也能看出USER_CURRENT和USER_CURRENT_OR_SELF的区别,后者还要将targetUser设置为callingUser. 不是USER_CURRENT_OR_SELF的情况会抛出异常,
可见使用USER_CURRENT_OR_SELF何时都能成功返回.
  5 制定allowAll为false 则还要进一步对targetUserId进行检查(错误的负数) 有问题抛出异常
  6 调用者是shall 并且targetUserId >= UserHandle.USER_SYSTEM,还要检查hasUserRestriction,用户是否被限制.
  被限制抛出异常, 这里检查的受限条件是DISALLOW_DEBUGGING_FEATURES
  7最后返回targetUserId,这里可以看出 asUser的检查,注意关键字asUser就是这些

总之记住这个函数是用于确定caller能有以targetUser运行,这个函数就容易分析了
    
  

3 final 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, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
TaskRecord inTask, String reason)
1 检查intent中不能传递文件描述符
2 获取是否指定了ComponentName
3 创建一个短暂的intent
4 为了不修改客户端的intent又创建了一个intent
5 对即时应用的处理,先不去管它
6 PMS解析Intent
7 如果解析到的ResolveInfo为空
如果user是其他用户的个人资料,则会进行如下处理
   1 如果父用户已经解锁,本用户还没有解锁,则查询带directBoot的组件,再次查询一次(因为现在设备其实是已经解锁的).
8 获取 ResolveInfo 对应的ActivityInfo
  ActivityStackSupervisor–>ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
ProfilerInfo profilerInfo)
1 拿出ResolveInfo的ActivityInfo,如果不为空做如下处理
   1 设置intent的Component 因为现在已经明确了组件(其实我们还需要关注有多个activity符合条件的情况,这部分可能被拿到了PMS中后面关注)
    2 如果不是system进程的组件对debug的flags做一些处理,由此可见system进程是不能debug的
    1 ActivityManager.START_FLAG_DEBUG (am start -D ) : AMS->setDebug()
    调用这要有SET_DEBUG_APP的权限
    2 参数是否要持久化,如果需要则设置settingProvider的Settings.Global.DEBUG_APP为要启动组件的packageName, Settings.Global.WAIT_FOR_DEBUGGER设置成等待的状态
    3 设置mDebugApp = packageName;
mWaitForDebugger = waitForDebugger;
mDebugTransient = !persistent;
并且强制停止要启动的activity对应的应用,方便调试(这与我们在设置中设置的调试app的功能其实效果是一样的,就是让应用停下来以便抓取应用启动的信息,调试应用启动)
3 ActivityManager.START_FLAG_NATIVE_DEBUGGING
设置mNativeDebuggingApp 为要调试的包名(其实前半还有个权限检查的过程,无关紧要)
4 START_FLAG_TRACK_ALLOCATION
设置mTrackAllocationApp的包名
5void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo) 这可以帮我我们理解profileInfo的作用
    1 检查权限如果系统不允许调试进程也不允许条四抛出安全异常
    2 设置prrofileApp为 应用包名
    3 关闭profileFd 新创建ProfilerInfo(拷贝构造)
    4 mProfileType设置为0 
到目前位置我们只知道了如何设置这下属性还不知到具体有何作用,不用着急我们会分析的
2 从intent中拿出LaunchToken设置到ActivityInfo.launchToken中,用于instant run我们先不去分析
8 这里ActivityInfo就获取到了
9  创建了一个参数bOptions的副本ActivityOptions (用于传递一些操作的操作)
10 计算callingUid 和callingPid
1首先从bind里面获取pid,uid(我这里不仅产生的怀疑,这么做真的安全吗).
2 如果callingUid>=0 设置callingPid = -1,否则的话如果caller == null ,设置callingPid和callingUid为bind中获取到的pid,uid , 在否则设置 callingPid = callingUid = -1; 
  为什么要这么做呢,主要就是因为有startActivity有几个变体,比如startActivityAsCaller,startActivityAsUser
11 获取到mFocusedStack , 判断参数传递的globalConfig和当前焦点栈的Config是否相同,并把结果设置到 stack.mConfigWillChange变量中,  globalConfig主要在startActivity变体函数startActivityWithConfig中传递,用于切换ui模式(carmode),后面我们会看到对它的处理
12 到这里就清除binder的calling信息,后面则不需要权限校验,所有的操作都是以system进程执行
13 如果应用指定了privateFlags标志有指定了PRIVATE_FLAG_CANT_SAVE_STATE的应用(Application属性cantSaveState ),这种应用被称为heavy-weight,生命周期略微不同
   1 对这个属性的处理有限要判断应用的进程名称是否和包名相同.根据注释说的可能有一个重量级组件进程已经在运行,检查是否已经有一个重量级组件进程在运行(可见系统中只能运行一个heavy-weigh进程)
1  获取系统中的heavy-weigh进程,进程的ProcessRecord保存在mService.mHeavyWeightProcess变量中
2 如果当前运行的heavy进程与要启动activity的宿主进程不是一个进程
   1 如果callerApp不存在抛出异常
   2 callerApp不为空,就会把intent换成启动HeavyWeightSwitcherActivity,用于选择HeavyWeight
 

14到这里完成了一些小插曲的处理之后就可以真正的去启动Activity了,首先创建一个传出参数ActivityRecord[] outRecord = new ActivityRecord[1], 这里使用数组就是为了做传出参数
15 我们先看从startActivityLocked返回之后的处理,首先恢复了Binder信息
16 判断stack.mConfigWillChange检查config是否发生变化,在这里更新config 为什么在这里更新我们先留作一个疑问,根据注释桑说是 因为我们正在等待当前的activity pause,并且还没有启动下一个activity(看似这个时机很合适啊),我们要关注从startActivityLocked返回到底是不是这个时机
17 如果返回结果不为空则 设置outResult.result = res (WaitResult outResult),之后对结果进行处理, 其实这个WaitResult 比较无关紧要,就是提供了启动的一些信息用于记录启动情况. 在系统中只有使用shell am启动的时候会加上这个参数, 所以我们只是简单的分析下
1 如果Activity启动成功,返回的结果为ActivityManager.START_SUCCESS,就把她加入到 mSupervisor.mWaitingActivityLaunched中,然后 wait,指导utResult.result==START_TASK_TO_FRONT(移动到了前台) 或者启动超时,或者outResult.who != null(真的启动了activity)否则就一直等,最后如果outResult.result == START_TASK_TO_FRONT表示没有启动activiy只是移动到了前台,这样设置返回结果为START_TASK_TO_FRONT
2 如果res == START_TASK_TO_FRONT 表明没有真正启动activity做如下处理
如果activity已经正常显示了(应该是这个activiy本身就在可见的栈顶) 就设置outResult.timeout = false;
outResult.who = r.realActivity;
outResult.totalTime = 0;
outResult.thisTime = 0;
否则的话还要等待activity变成显示状态,这就还要等了,直到超时或者activity已经显示起来
3 这里我们可以得到这样的信息,Activity的显示和启动都是异步的(包括后后台拿到前台和新启动) ,等待的记录由mWaitingActivityLaunched和mWaitingForActivityVisible维护,一个等待启动,一个等待显示
18 最终记录log mSupervisor.mActivityMetricsLogger.notifyActivityLaunched(res, outRecord[0]) 并返回,注意这里我们实际上不一定已经看到了activity,也不一定执行了activity的onCreate方法
19返回启动的结果

ActivityStarter—–>
int startActivityLocked(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,
ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
TaskRecord inTask, String reason)

1 设置下面三个值
mLastStartReason = reason;
mLastStartActivityTimeMs = System.currentTimeMillis();
mLastStartActivityRecord[0] = null;

2 startActivity(caller, intent, ephemeralIntent, resolvedType,
aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
container, inTask)
3 设置返回结果返回
ActivityStarter —->
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,
ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
TaskRecord inTask) {
1 如果存在caller,重新获取callingPid 和callingUid ,如果指定caller进程却不存在,抱歉返回START_PERMISSION_DENIED错误
2 对sourceRecod 和 resultRecord 和进行计算. 这里分为指定FLAG_ACTIVITY_FORWARD_RESULT和不指定这个标志的情况. 这里说明如果最终计算得到的resultRecord不为空的话,代表需要接收结果. 下面这幅图表述在有FLAG_ACTIVITY_FORWARD_RESULT和没有该标志的时候的sourceRecod,resultRecord 和新启动的activity(newactivity)的关系
这里写图片描述
这张图片里面包含两幅图,分别代码指定FLAG_ACTIVITY_FORWARD_RESULT和没有指定FLAG_ACTIVITY_FORWARD_RESULT情况下的关系. (省略不需要接收且过的情况)
3 对如下三种情况的处理
1 没有找到对应的component返回START_INTENT_NOT_RESOLVED
2 没有找到ActivityInfo的情况返回START_CLASS_NOT_FOUND
3 要启动的activity是从voice task过来的但是没有该activity不支持voice的情况返回START_NOT_VOICE_COMPATIBLE
4 不支持void的intent START_NOT_VOICE_COMPATIBLE
4 对于三种不能启动的情况设置给接收结果的activity发送 RESULT_CANCELED,然后退出函数
5 一系列权限的检查,包括权限, monkey和防火墙规则的检查 和拦截器的检查,另外拦截器检查还会过滤掉一些数据
6 最后对于检查不通过的发送RESULT_CANCELED,结束动画,并且返回START_SUCCESS,神不知鬼不觉的干事
7 mPermissionReviewRequired 弹出权限审查界面
8 通过上述检查就可以放心的启动了,所以这里对这次启动activity创建ActivityRecord数据
9 对AppSwitch的处理
AMS提供了两个函数,用于暂时(注意,是暂时)禁止App切换。为什么会有这种需求呢?因为当某些重要(例如设置账号等)Activity处于前台(即用户当前所见的Activity)时,不希望系统因用户操作之外的原因而切换Activity(例如恰好此时收到来电信号而弹出来电界面)。
AMS.stopAppSwitches()
· 此处控制机制名叫app switch,而不是activity switch。为什么呢?因为如果从受保护的activity中启动另一个activity,那么这个新activity的目的应该是针对同一任务,这次启动就不应该受app switch的制约,反而应该对其大开绿灯。目前,在执行Settings中设置设备策略(DevicePolicy)时就会stopAppSwitch。
· 执行stopAppSwitch后,应用程序应该调resumeAppSwitches以允许app switch,但是为了防止应用程序有意或无意忘记resume app switch,系统设置了一个超时(5秒)消息,过了此超时时间,系统将处理相应的消息,其内部会resume app switch。
resumeAppSwitches()
在resumeAppSwitches中只设置mAppSwitchesAllowedTime的值为0,它并不处理在stop和resume这段时间内积攒起的Pending请求,那么这些请求是在何时被处理的呢?
· 从前面代码可知,如果在执行resume app switch后,又有新的请求需要处理,则先处理那些pending的请求(调用doPendingActivityLaunchesLocked)。
· 在resumeAppSwitches中并未撤销stopAppSwitches函数中设置的超时消息,所以在处理那条超时消息的过程中,也会处理pending的请求。
10 startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
options, inTask, outActivity)

ActivityStarter—>
private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity)
startActivity函数也是十分简单的,我们来分析下
1 mService.mWindowManager.deferSurfaceLayout(); 锁定WMS 不让它进行重新的页面布局
2 startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity) 真正启动activit的函数在这里
3 判断如果activit没有启动成功,则做一些清理工作,把mStartActivity(也就是我们要启动的activity)从它的task中清除掉,因为对我们已经没什么用了,这不是一个健康的activity
4 mService.mWindowManager.continueSurfaceLayout() 释放我们的layoout锁定,允许WMS重新布局(这里如果幸运的话会执行一次wms布局)
5 对结果的处理 postStartActivityProcessing(r, result, mSupervisor.getLastStack().mStackId, mSourceRecord,
mTargetStack) 这个函数比较简单,我们先对它进行分析,也能从这个函数中窥探到一些startActivityUnchecked的作用
1 判断启动失败,则直接返回,如何判断启动失败??? FIRST_START_FATAL_ERROR_CODE <= result && result <= LAST_START_FATAL_ERROR_CODE 返回结果在-100~-1之间
2 判断启动且过如果是START_TASK_TO_FRONT(只是把activity的task移动到前台) 并且!mSupervisor.mWaitingActivityLaunched.isEmpty(),mWaitingActivityLaunched这个结合我们之前看到过,是在startActivityMayWait函数中,要等待返回结果的情况.我们会等到activit被显示后才去返回,那么这里如果startActivityMayWait集合里面包含元素,我想当前启动的activity也不会是mWaitingActivityLaunched集合中的Activity.
这里对每个mWaitingActivityLaunched中的还没有等到activity启动的WaitResult(也就是WaitResult.who == null)的那些组件,告诉他们w.result = START_TASK_TO_FRONT, 然后如果真的存在这样的组件,他们还wait在AMS上,执行notify唤醒他们,进入到下一个阶段(mWaitingForActivityVisible),这里其实也是比较奇怪的,他们真的能等到组件可见吗.我们分析中见分晓
3 对currentStack和targetstack的处理,这里的currentStack指我们启动的activity所在的栈,而targetStack为目标stack(也就是我们要把它放到哪个栈上), 如果我们现在在DOCKED_STACK栈上
1 找到homeStack . 如果homestack可见, 要切换到RecentActivity上面去显示应用(分屏),这种场景应该是这样的,当你在RecentActivity(桌面点击菜单键,进入systemui的多任务页面.)然后选择一个分屏.之后可能在桌面上又点击了这个在分屏页面的应用,或者从其他没有分屏的应用跳转到这个应用的情况, 这种情况就是从HOME_STACK_ 跳转到DOCKED_STACK的情况,所以ams的处理是马上启动RecentActivity到达分屏页面..
这个函数有意思的地方还有就是选择currentStack的时候,如果启动的activity没有在stack里面就把它的stack设置为mTargetStack,什么情况下一个Activity没有stack呢,我们后面分析的过程中解决这个疑惑,
2 处理完成这种情况就直接返回了
4 对PINNED_STACK的处理(也就是PIP画中画模式)
1 这里要处理的情况必须满足当前启动的activity在PINNED_STACK中,然后如果有下列清醒的要把做一些通知工作
1 这里处理的是指定了FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK 两个标志的情况下发生的化学反应. 要清除task那么当前activity就在栈顶了
2 启动结果为result == START_TASK_TO_FRONT
|| result == START_DELIVERED_TO_TOP 本来pip的activity就在播放现在由进行了启动它的操作. 所以放回进行一些通知操作
5 这里我们先不去管怎么处理的这种情况那么postStartActivityProcessing 函数就分析完成呢了

现在进入老大难函数进行分析
ActivityStarter—->
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {
函数大概200行对于我们应该小菜一碟.来吧

1 设置当前状态
 setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor)
ActivityStarter—->private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
boolean doResume, int startFlags, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
1 首先重置所有的状态

               mStartActivity = null;    要启动的ActivityRecord
                mIntent = null;        Intent
                mCallingUid = -1;      CallingUid   还记的这个值吗,调用者指定的callingUid>0的时候设置callingPid=-1 如果没有指定caller就是bind里面的值 ,也有可能为-1
                mOptions = null;       动画操作的一些辅助数据

        mLaunchSingleTop = false;       //activity是singletop启动?
        mLaunchSingleInstance = false;  //activity是singleInstant的
        mLaunchSingleTask = false;      //activity是singleTask的
        mLaunchTaskBehind = false;      // ???????????
        mLaunchFlags = 0;               // 启动的flags  intent中设置的

        mLaunchBounds = null;           // 大小 动画过程或者pip

        mNotTop = null;                 // 指定FLAG_ACTIVITY_PREVIOUS_IS_TOP标志时候如果当前要启动的activity已经在顶部也不把它当成顶部的activity. 
        mDoResume = false;              //  是否要显示,还记得吗?
        mStartFlags = 0;                //  和启动模式无关,启动过程做一些处理的标志
        mSourceRecord = null;           // 调用startactivity的是谁
        mSourceDisplayId = INVALID_DISPLAY;   // 要启动在哪个屏幕上display

        mInTask = null;                  // 指定启动的task 
        mAddingToTask = false;           // 是否要添加到task中(不添加的情况是直接拿到前台就好了)
        mReuseTask = null;                // 复用task只有当mInTask不为空且sourceRecord为空且mInTask在真实的栈中的清空下该值等于mInTask

        mNewTaskInfo = null;               // 当sourceRecord已经销毁,我们要启动新的task但是需要使用sourceRecord的信息作为新的task信息,所以这里用作记录这些信息
        mNewTaskIntent = null;             //同上记录intent
        mSourceStack = null;               //sourceRecord所在的stack

        mTargetStack = null;               
        mMovedOtherTask = false;
        mMovedToFront = false;
        mNoAnimation = false;           // 是否允许执行动画
        mKeepCurTransition = false;
        mAvoidMoveToFront = false;      

        mVoiceSession = null;          // 语音相关 不关注    
        mVoiceInteractor = null;       //同上

        mUsingVr2dDisplay = false;

这些状态还是比较多的,大多数都是启动参数,我们来标注一下
reset之后
1 设置mStartActivity , mIntent,mOptions,mCallingUid,mSourceRecord,mVoiceSession,mVoiceInteractor
2 mSourceDisplayId = getSourceDisplayId(mSourceRecord, mStartActivity);
1如果要启动的activity是VR Activity 则只能启动在DEFAULT_DISPLAY,返回DEFAULT_DISPLAY
2 如果 如果ams的mVr2dDisplayId是可用的返回它
3 如果上如条件不满足并且sourceRecord不为空返回它的displayId
4 否则返回DEFAULT_DISPLAY
看来vr的优先级还挺高,有时间好好研究下
4 mLaunchBounds = getOverrideBounds(r, options, inTask) 获取到activity的大小,对于这个参数我们只需要知道是设置大小的就可以了,一般通过ActivityOptionssetLaunchBounds(new Rect(0, 0, 500, 500)) 函数设置
设置这个参数要有一系列限制
1 要求当前activity或者task支持resize. 设置resizeableActivity=true ,并且activity要启动在PINNED_STACK中或者支持mSupportsFreeformWindowManagement模式
所以该功能的用处也不难分析就是设置activity的大小 ,注意这和普通模式下的activity可能是不同的,它可能不会影响焦点的获取
5 三大launchMode,默认是标准的
mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
6 mLaunchFlags = adjustLaunchFlagsToDocumentMode(
r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags()); 这里计算mLaunchFlags的地方,这里我们看下它主要是依据什么设置以及和mStartFlags的区别
1 如果intent中指定了 Intent.FLAG_ACTIVITY_NEW_DOCUMENT但是当前的activity设置了启动模式为LAUNCH_SINGLE_INSTANCE或者LAUNCH_SINGLE_TASK,那么配置的启动模式生出,这是就要去掉Intent传递的flags , launchFlags &=
~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK)
2 没有冲突的话 如果Activity设置了documentLaunchMode 做如下处理,见代码
switch (r.info.documentLaunchMode) {
case ActivityInfo.DOCUMENT_LAUNCH_NONE:
break;
case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
break;
}
3 最后返回修复好的launchFlags, 由此可见launchFlags 是intent结合activity设置的documentLaunchMode和launchmode结合设置的
7 mLaunchTaskBehind 处理,如果启动参数中指定了LaunchTaskBehind,但是当前启动模式是huozmLaunchSingleTask或者mLaunchSingleInstance,又或者intent中指定了FLAG_ACTIVITY_NEW_DOCUMENT这个flags,这种情况是不能在后台启动的. 所以设置为false
那么这个mLaunchTaskBehind有什么用呢,从注释的描述上来看是启动一个activity而不把它放在前台,而是做多任务中把它放在和调用它的activity的task的前面的task里面. 不知道有毛用,在整个系统中只是看到有一个测试用例有使用这个特性
8 sendNewTaskResultRequestIfNeeded() 函数 ,如果启动的flags指定了FLAG_ACTIVITY_NEW_TASK标志,这可能要新启动一个task或者设计到task的切换,是不是有返回结果的,所以个resultTo activity发送一个RESULT_CANCELED消息
9 如过启动flags指定了FLAG_ACTIVITY_NEW_DOCUMENT 并且不需要接收结果和要加上FLAG_ACTIVITY_NEW_TASK标志,这其实就是把FLAG_ACTIVITY_NEW_DOCUMENT转换为FLAG_ACTIVITY_NEW_TASK, 看来FLAG_ACTIVITY_NEW_DOCUMENT只是对FLAG_ACTIVITY_NEW_TASK在Document层面的一种包装,这里对Document api的说明google的官方文档写的比较清楚,这里是中文翻译的地址https://www.aliyun.com/jiaocheng/59462.html
10 如果mLaunchTaskBehind成立或者DOCUMENT_LAUNCH_ALWAYS标志加上FLAG_ACTIVITY_NEW_DOCUMENT属性,还要设置FLAG_ACTIVITY_MULTIPLE_TASK 标志,这些全都是围绕Document展开的
11 如果intent带有标志FLAG_ACTIVITY_NO_USER_ACTION,则设置mSupervisor.mUserLeaving为false,表明该activity不是由用户启动的,所以当其他activity被覆盖的时候不需要执行onUserLeaving函数
12 设置doresume函数,我们之前有见多doresume为false的情况表示不需要显示这个activity,比如那些在禁止appswitch期间启动的activity,而后面由于其他activity的启动导致pending的activity要被启动,这些pendingactivity是不需要被resume的因为已经被覆盖.
如果这个activity当前不需要被启动,或者user还没准备好或者不存在了,设置r.delayedResume = true;
mDoResume = false; 表示不需要启动
13 如果指定了task并且设置了TaskOverlay,设置 ActivityRecord.mTaskOverlay = true; 这个参数我们还不知道什么含义,先不去管后面看到再去说明
1 如果没有设置mTaskOverlayCanResume
1 调用mSupervisor.anyTaskForIdLocked 获取taskid
ActivityStckSupervisor–>
TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode, int stackId)
对于这个函数参数必须是matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE &&stackId != INVALID_STACK_ID 做一个小小的检查
从注释上可以看到函数的作用是从某个栈上找到指定的taskid对应的taskrecord
1 遍历活跃的Display,stack,如果找到直接返回,也不用管指定的stackid
2 如果没有在所有display上找到则看看匹配模式是否有什么可以做的
3 matchMode == MATCH_TASK_IN_STACKS_ONLY 顾名思义tack不在stack上就直接返回null
4 从mRecentTaskstask中找,如果没有找到返回空
5 从recet中找到了,如果模式是MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,不需要恢复则直接返回
6 对MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE模式的处理,其实就是AND_RESTORE部分的处理,使用restoreRecentTaskLocked(task, stackId)函数处理
restoreRecentTaskLocked(task, stackId)函数还很长烦人
ActivityStckSupervisor–>
boolean restoreRecentTaskLocked(TaskRecord task, int stackId) 函数的作用是从recenttask中恢复一个task到指定的stack
1 如果task不是一个静态stack (0-6),直接从tack中恢复.这种情况是根据activitytype恢复的,有两种情况,一种是invalid的stackid,是没有指定的情况,另外一种情况是之前这个task是其他屏幕上的stack,stackid>6的情况也恢复到默认的display上,这我们也可以窥探到一点多屏幕的管理的实现
2 如果1 不满足, 目标stack是DOCK_STACK,但是tack又不支持分屏,这种情况白搭啊,所以设置stackId = FULLSCREEN_WORKSPACE_STACK_ID,也就是放在正常的应用所在的stack中
3 获取之前task所在的stack
如果和之前是一个tack这就啥都不用做了直接返回restore成功
否则的话从当前的stack上移除这个task,我们无奈的看到这个函数还是超级长,在这里分析的话已经缩进的很困难了,所以我们拿到后面标号0x1的地方分析.
4 从当前task中移除之后就该找目标stack了getStack(stackId, CREATE_IF_NEEDED, !ON_TOP), 这又是一个很复杂的函数我们放在0x03节分析
5 如果获取的stack为空返回false
6 然后把task放到这个stack中,这又是一个复杂的函数放到0x4 分析
7 创建windowContainer 这还是个复杂的函数放到0x5分析
8 对task中的每一个activity创建windowcontroller,这也是复杂函数放到0x6分析
7 返回task
2 到这里从anyTaskForIdLocked返回,这里我们挖到了一座大山而大多数情况都是直接在当前存在的stack中找到task,不需要从recent中恢复
3 如果到的task栈顶的activity不处于显示状态,设置mDoResume = false;
mAvoidMoveToFront = true;
所以这里可以看出来mTaskOverlayCanResume为false是避免移动后面的task到前面而覆盖当前显示的task,这种情况避免所以设置这连个变量.
14 对FLAG_ACTIVITY_PREVIOUS_IS_TOP的标志的处理目前不知道什么含义,如果标志为1,则设置mNotTop为当前要启动的activity,猜想是不把它放在task顶部
15 设置intask
16 如果指定的task不在inRecents中,说明这个task已经不存在了,设置mInTask为null
17 设置startFlags为参数中的flags
18 START_FLAG_ONLY_IF_NEEDED 标志的作用是给那些当前就在task顶部并且设置了singleTask和singleInstance的activity传递一个intent处理,否则和普通的activity启动没有任何区别,这里我们不禁产生疑问, singleInstance 的activity如果就在栈顶是不是就不需要处理新intent了? 所以这里处理了对于调用者和启动者不是同一个activity的情况就去掉这个标志
19 设置mNoAnimation变量表示是否允许执行动画

2 computeLaunchingTaskFlags 重新修正launchflags , 主干上有两个路径
1 如果调用者Activity不存在,并且指定了task,且task存在对应的stack
1获取指定的task的baseIntent 和指定的task的rootActivity(task中最底部没有销毁的activity)
2 如果baseIntent是空的,直接抛出异常 并不知道是什么情况发生
3 如果是启动launchSingleInstance || mLaunchSingleTask 模式的activity
1baseIntent和我们要启动的activity不一样则抛出异常(对于指定task的情况要求比较严格,task必须正确)
2 如果rootActivity是空的也会抛出异常
3 由此可以看出来,如果mLaunchSingleInstance || mLaunchSingleTask 必须存在baseIntent和rootActivity
4 经过上面对mLaunchSingleInstance || mLaunchSingleTask的检查,抛出一些不正常的情况,下面又做了如下检查
1 task的root为空,这种情况需要对task进行一些调整,既然要求启动activity在一个task中,但是这个task是空的,我们就要对这个task做一些初始化操作,把他变成以我们为基准的task
首先对于一个新task清楚 mLaunchFlags的FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
| FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS 位 ,然后添加原来task存在的那些标志,设置到task中,我们对task的baseintent的管理还所知很少,所以还不能详细说明它的作用,当我们遇到的时候再去分析,但是我们这里可以确定task是存在的所以设置变量mAddingToTask = true 表示不需要新创建task,直接放到存在的task中就好,并且我们就是这个task的baseIntent的主人
2 如果指定了task并且启动这个activity设置了mLaunchFlags设置了FLAG_ACTIVITY_NEW_TASK标志表明我们希望新启动一个task,但是这个task已经存在,所以就把它拿到上上面来,并发送一个新的intent给它,所以设置mAddingToTask = false,由此看来我们不需要启动一个新的activity,看来指定inTask要在一定的语义下完成,我们前面的分析一定遗漏了一些条件. 我们还需要重新弄醒出inTask设置的条件, 否则我们没有办法弄明白它
3 在1,2 都不满足的条件也就是说我们的tack中已经存在activity但是没有指定FLAG_ACTIVITY_NEW_TASK标志,这种情况要在task中新建activity,为什么活这样的呢?????
5 最终设置 mReuseTask = mInTask 到这里我们确定了要重用task
6 这里可以总结下指定inTak的情况必须不存在sourceActivity,另外还要task真实的存在stak中. 另外就是如果是一个空的task就要创建activity,而不是空的并且指定了FLAG_ACTIVITY_NEW_TASK即使activity不存在也不会重新创建. 不指定FLAG_ACTIVITY_NEW_TASK且task不是空的,就会创建activity. 这里第二个条件下可能只是把task带到前台
2 与1 条件相反(也就是没有指定task或者指定的task有问题,或者sourceRecord被指定)
1 mInTask 设置为null
2 对于resloveraCTIVITY或者noDisplay要并且mSourceRecord不为空并且mSourceRecord在freefrom 的task上要把这样的activity放在源sourceRecord上面,这是很合理的,因为是一个free的情况,这种情况必然要把activity放到task上所以设置mAddingToTask = true
3 最后如果mInTask 是空的 ,也就是没有指定task或者被我们修正为null的情况
1 如果mSourceRecord为空的情况不是由于其他activity启动你能放到sourceRecord的task中所以设置mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK,一遍后面可以为它创建一个task
2 对于mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE的情况,sourceRecord的task必须只能包含这一个activity,所以也不能把要启动的activity放到这个task中,这里也要设置mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK
3 对于要启动的activity是mLaunchSingleInstance || mLaunchSingleTask 要添加FLAG_ACTIVITY_NEW_TASK 以便不把它启动到sourceRecord所在的task
4 这里我们在总结下inTask和非inTask的情况,如果指定的task得到复用,设置mReuseTask = mInTask, mAddingToTask代表要不要放到task中,如果为false则不会启动新的 activity, FLAG_ACTIVITY_NEW_TASK表示不把activity放到当前task中

3 private void computeSourceStack()
1 mSourceRecord == null 那么sourceStack也不存在,直接返回
2 如果mSourceRecord没有关闭,直接设置mSourceStack = mSourceRecord.getStack() 返回
3 这种情况对应sourceRecord正在关闭或者sourceRecord不存在(注意不存在的情况下FLAG_ACTIVITY_NEW_TASK必然不为0),如果没有设置FLAG_ACTIVITY_NEW_TASK标志,要添加上FLAG_ACTIVITY_NEW_TASK以便不在原来的task上启动
设置mNewTaskInfo = mSourceRecord.info,这是因为吊起我们的activity的task已经被销毁,所以我们不能盲目的把它添加到原来的task中,但是我们可以保留原来的ActivityInfo,以便我们在创建新的task的时候恢复它的信息,同样我们
也记录了原来sourceRecord的intent信息
4 最后设置 mSourceRecord = null;
mSourceStack = null;

4 设置新的intent标志
5 getReusableIntentActivity() 获取可重用的activity
1 首先获取是否需要重用activity.该变量通过putIntoExistingTask变量反应,也就是要满足的条件为
1 mLaunchFlags & FLAG_ACTIVITY_NEW_TASK!=0 表示要启动新的task
2 mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0 如果不为0 表示要启用一个全新的task,所以这里要求不能有该标志
3 mInTask == null 我们前面已经看到,去过mInTask情况遇到mLaunchSingleInstance || mLaunchSingleTask 就要要求task中没有任何activity,并且task的baseIntent指定的组件和我们要启动的相同
另外如果指定了FLAG_ACTIVITY_MULTIPLE_TASK标志并且栈中存在activity则表示不需要启动新的activity,只是把他的task拿到前台就可以了. 所以对于inTask的情况要么启动一个新的activity,要么存在一个新的task(没有activity).
4 mStartActivity.resultTo == null.
2 putIntoExistingTask = (1 || 2) &3 &4 对于这种情况如果已经存在合适的task,需要将activity放到对应task,getReusableIntentActivity只是用于找到对应的task,那么返回activity是做什么用的呢,只是用于找到我们的activity应该放在task上的位置.

6 好了现在可以进入reusedActivity != null的情况进行分析了
1 如果task被锁定 还要检查是否可行,不可行直接返回START_RETURN_LOCK_TASK_MODE_VIOLATION
2 如果mSatrtActivity没有task则设置它的task为reusedActivity.getTask()因为我们要把它放在这个task中
3 如果找到的task没有intent把我们的intent设置给它
4 如果指定了FLAG_ACTIVITY_CLEAR_TOP

0x1

ActivityStack—->
void removeTask(TaskRecord task, String reason, int mode) 现在看一下栈的管理
1 对这个task上的所有activity做处理
移除所有超时的消息,和确认mResumedActivity 和mPausingActivity是否被移走,移走后要设置为null,移除的超时消息包括如下 mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r)
stack.mHandler.removeMessages(LAUNCH_TICK_MSG) 这里LAUNCH_TICK_MSG好像是做统计用的 我们后面分析
2 从stack的mTaskHistory找到要移除的task的位置
3 如果当前task下面是桌面的并且当前task不是这个stack上最上面的task,要把这个task上面的task设置返回到HOME_ACTIVITY_TYPE,这主要是通过task的mTaskToReturnTo变量记录(还有个条件是返回到语音助手stack的task,看来助手的task也是比较特殊的)
4 从mTaskHistory中移除task
5 把task下的activity从mLRUActivities中移除
6 updateTaskMovement 更新一些操作的信息
7 如果移动的mode为 REMOVE_TASK_MODE_DESTROYING ,并且task下存在activity,则销毁他们
1 带有isVoiceSession的 回调taskFinished
2 如果这个task没有显示过,或者被配置不在自动列表显示,或者是由语音启动的,则从mRecentTasks中移除, 恢复成好像是一个新创建的task一样
3 清理windowContainer相关数据(用于wms排序吗<

0X2

ActivityStack—>
private boolean adjustFocusToNextFocusableStackLocked(String reason, boolean allowFocusSelf)
第二个参数allowFocusSelf 看意思应该允许自己是focus???? 我们带着疑问去读
1 首先调用 mStackSupervisor.getNextFocusableStackLocked(
allowFocusSelf ? null : this) 函数,参数可能是自身也可能是null,所以我们的疑问要带到这个函数去解决

0x03

ActivityStackSupervisor–>
protected T getStack(int stackId, boolean createStaticStackIfNeeded,
boolean createOnTop)
1 如果mActivityContainers中存在对应的stackId的ActivityContainer,直接返回ActivityContainer中的stack
2上面1中没有找到对应的tack. 如果传递的参数不要求创建stack 或者不是静态的stackId直接返回null(为啥动态的不能在getStack 中创建???)
3 如果要创建的栈是DOCKED_STACK则需要RECENTS_STACK 这里面二者有什么关系需要我们在分析到的时候具体分析
4 ActivityStackSupervisor–>createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop) 在默认的屏幕上创建
  1 getActivityDisplayOrCreateLocked(displayId) 获取display (见0x6)
  2 如果没有对应的display直接返回null
  3 创建ActivityContainer(ActivityContainer是对ActivityStack的管理,并提供了一些方法,可以作为binder对象给客户端调用)
   1 设置stackId, mActivityDisplay. mIdString(ActivtyContainer{” + mStackId + “})
   2 createStack(stackId, onTop)
   ActivityStackSupervisor–> protected void createStack(int stackId, boolean onTop)
   1 对于PINNED_STACK创建PinnedActivityStack,其他的创建ActivityStack,由于PinnedActivity继承自AActivityStack,并且PinnedActivity用到的比较少(目前只知道PIP)
   new ActivityStack(this, mRecentTasks, onTop) 从代码结构上不建议这么写,因为可能存在线程安全问题中的this指针逃逸,我们老看看ActivityStack的构造(如果觉得代码比较难其实是我们来不了解整体的需求)
   ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
RecentTasks recentTasks, boolean onTop )  
    主要就是个自己的数据结构赋值,

                 mActivityContainer = activityContainer;  //ActivityStack对应一个容器
                            mStackSupervisor = activityContainer.getOuter(); 
                            mService = mStackSupervisor.mService; //AMS
                                mHandler = new ActivityStackHandler(mService.mHandler.getLooper()); //AMS.MainLooper
                            mWindowManager = mService.mWindowManager; //WMS
                            mStackId = activityContainer.mStackId;  
                            mCurrentUser = mService.mUserController.getCurrentUserIdLocked();
                            mRecentTasks = recentTasks;
                            mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
                                    ? new LaunchingTaskPositioner() : null; //位置
                            final ActivityStackSupervisor.ActivityDisplay display = mActivityContainer.mActivityDisplay;  
                            mTmpRect2.setEmpty();
                            mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
                                    mTmpRect2);
                            activityContainer.mStack = this;
                            mStackSupervisor.mActivityContainers.put(mStackId, activityContainer);
                            postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);

    这里面有几个数据结构需要说明ActivityDisplay是AMS这段用于管理Display的数据结构,同样WMS端还有个DisplayContent管理Display.
    WindowContainerController 用于AMS个WMS之间的Stack管理,这个类控制了在AMS端的ActivityStack和WMS端的TaskStack之间的关系和交互   
    WindowContainerController的创建清参考0X8
   

0X4

void addTask(final TaskRecord task, final boolean toTop, String reason)

0x5

void createWindowContainer(boolean onTop, boolean showForAllUsers)

0x7
createVirtualActivityContainer

0x8

ActivityStack–>T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
}
@VisibleForTesting
public StackWindowController(int stackId, StackWindowListener listener,
int displayId, boolean onTop, Rect outBounds, WindowManagerService service) { AMS和WMS真实打断骨头连着筋
1 调用父类构造方法,父类是WindowContainerController,这里值得注意的是设置mWindowMap为WMS.mWindowMap.这个map用于管理IWindow->WindowState, 设置mRoot为WMS的mRoot(管理DisplayContent的工具类)
2 获取对应的DisplayContent,前面说过DisplayContent对应AMS端的ActivityDisplay两者的关系就是displayId相同. 这里只是从RootWindowContainer中找到对应的display,找不到就抛出异常
3 创建WMS段的TaskStack到DisplayContent中,这个操作是通过DisplayContent->TaskStack addStackToDisplay(int stackId, boolean onTop) 函数完成的,我们在0x09中分析

TODO :
mSupervisor.mActivityMetricsLogger.notifyActivityLaunching()// Activity度量日志器
ephemeral installer 分析
mProfilerInfo | mDebugApp mWaitForDebugger mDebugTransient  mNativeDebuggingApp mTrackAllocationApp 调试用的一些属性
stack.mHandler.removeMessages(LAUNCH_TICK_MSG)
ActivityContainer

什么情况下一个Activity没有stack呢,????

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值