【Android Framework核心面试题】 LoginActivity配置的launchMode是何时解析的?

startActivity(MainActivity.this,LoginActivity.class); LoginActivity配置的launchMode是何时解析的?

这道题想考察什么?

考察同学是否Activity的launchMode熟悉。

考生应该如何回答

启动模式相关源码都在ActivityStarter#startActivityUnchecked(), 我们对这个函数来分析一下:
注意下两个主要的属性 mLaunchFlags,mLaunchMode,后面的源码也是主要对着两个属性分析。
mLaunchFlags 包括启动的flag,比如FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_CLEAR_TASK等,作用是表明如何去启动一个Activity,对栈的选择和处理。
mLaunchMode 表示启动模式,比如LAUNCH_SINGLE_INSTANCE,LAUNCH_SINGLE_TASK等。

初始化工作
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {
    
    // 初始化  mLaunchFlags  mLaunchMode
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);
        // 一.计算 mLaunchFlags
        computeLaunchingTaskFlags();
        //赋值 mSourceTask
        computeSourceStack();
        mIntent.setFlags(mLaunchFlags);
        ... ... ... 

我们看下 computeLaunchingTaskFlags 方法

   private void computeLaunchingTaskFlags() {
    
        ... ... ...
        
        // mSourceRecod 指的是 启动者,(注意区别于 被启动者 mStartActivity) mSourceRecord为null,表示我们不是从一个Activity来启动的
        // 可能是从 一个Service 或者 ApplicationContext 来的
            if (mSourceRecord == null) {
                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
                     //mInTask mSourceRecord 都为null, 表示 不是从一个Activity 去启动另外一个Activity,所以不管什么    
            //都加上 FLAG_ACTIVITY_NEW_TASK
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                }
            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
        // 如果 启动者 自己是 SINGLE_INSTANCE , 那么不管被启动的Activity是什么模式,mLaunchFlags 都加上 FLAG_ACTIVITY_NEW_TASK,
        // 这个新 Activity 需要运行在 自己的 栈内
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            } else if (isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
        //如果launchMode是  SINGLE_INSTANCE 或者 SINGLE_TASK;  mLaunchFlags 添加  FLAG_ACTIVITY_NEW_TASK
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            }
    }

如果启动者mSourceRecord=null,例如在一个Service启动了一个Activity,那么mLaunchFlags增加FLAG_ACTIVITY_NEW_TASK
如果启动者mSourceRecord是一个 SingleInstance 类型的Activity,那么被启动者的mLaunchFlags就会加上 FLAG_ACTIVITY_NEW_TASK
如果被启动者mStartActivity是SINGLE_INSTANCE或者SINGLE_TASK 类型的Activity,被启动者的mLaunchFlags都会加上FLAG_ACTIVITY_NEW_TASK

getResuableIntentActivity

这一部分还是startActivityUnchecked方法的一个片段,紧接着第一部分的源码

        //还是在 startActivityUnchecked 里面
        ... ... ...
        // 1. 为SINGLE_INSTANCE查找可以复用的Activity  
        // 2. 为 只有FLAG_ACTIVITY_NEW_TASK并且没有MULTI_TASK的
        // 3. SINGLE_TASK 查找可以添加的栈
        ActivityRecord reusedActivity = getReusableIntentActivity();
        ... ... ...

我们先跳出startActivityUnchecked方法,去查看下getResuableIntentActivity方法。

    private ActivityRecord getReusableIntentActivity() {

    //putIntoExistingTask为true的条件
    //1.当启动模式为SingleInstance;
    //2.当启动模式为SingleTask;
    //3.使用了Intent.FLAG_ACTIVITY_NEW_TASK标签,并且没有使用FLAG_ACTIVITY_MULTIPLE_TASK标签
        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);

        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
        ActivityRecord intentActivity = null;
        
        if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
        
            ... ... ...
            
        } else if (putIntoExistingTask) { //putIntoExistingTask为true时的策略
            if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {

        // SINGLE_INSTANCE 模式下去寻找,这里目的是findActivityRecord
               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
                       mStartActivity.isActivityTypeHome());
            } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
                ... ...
            } else {
                //这里要区别于singleInstance调用的方法!!!这里目的是findTaskRecord
                // Otherwise find the best task to put the activity in.
                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
            }
        }
        return intentActivity;
    }

mLaunchMode是SingleInstance时,走mSupervisor.findActivityLocked;剩余情况下,比如我们的mStartActivity是一个standard模式的Activity,且只加上了FLAG_ACTIVITY_NEW_TASK的flag,会执行mSupervisor.findTaskLocked。

最合适的可重用栈
   void findTaskLocked(ActivityRecord target, FindTaskResult result) {
        ... ... ...
        // 注意这是倒序遍历 mTaskHistory
        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
        
            ... ... ...

            } else if (!isDocument && !taskIsDocument
                    && result.r == null && task.rootAffinity != null) {
                //检查 是不是 相同的  taskAffinity
                if (task.rootAffinity.equals(target.taskAffinity)) {
                    //当我们找到taskAffinity符合的栈之后,并没有立马break,而是继续去寻找,说明task的index越小,表示更适合
                    result.r = r;
                    result.matchedByRootAffinity = true;
                }
            } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
        }
    }

最合适的栈 需要满足两个条件
1.Activity的taskAffinity和我们的task的rootAffinity相等
2.不同的task的rootAffinity可能是相等的,倒序遍历找到index最小的,也是最合适的

reusedActivity的处理

我们接着分析startActivityUnchecked代码

        // 三. 利用可以 复用的Activity 或者 复用栈
        if (reusedActivity != null) {
        
            ... ... ...
            
            // 如果是 SingleInstance 或者 SingleTask 或者  含有 FLAG_ACTIVITY_CLEAR_TOP 标识
            //我们可以判断出来  SingleInstance 或者 SingleTask  含有 FLAG_ACTIVITY_CLEAR_TOP 的效果
            if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                    || isDocumentLaunchesIntoExisting(mLaunchFlags)
                    || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {

                //拿 reuseActivity 的栈
                final TaskRecord task = reusedActivity.getTask();
            
        // 比如 SingleTask  移除要启动的Activity之前的所有Activity
                final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
                        mLaunchFlags);

                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);
                    }
                //这里是 SingleInstance 或者 SingleTask ,会执行onNewIntent
                    deliverNewIntent(top);
                }
            }
            
            ... ... ...
            
            //启动者和被启动者是同一个
            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) {
                //这里会去判断几种情况 singleTask singleInstance 和 singleTop
                setTaskFromIntentActivity(reusedActivity);

                if (!mAddingToTask && mReuseTask == null) {
                    
                    //singleInstance singleTask 都会走这里
                    //1.比如要启动的Activity是singleTask,且刚好在reusedActivity的栈内
                    //2.或者一个singleInstance模式的Activity再次被启动
                    resumeTargetStackIfNeeded();
                    if (outActivity != null && outActivity.length > 0) {
                        outActivity[0] = reusedActivity;
                    }
                    return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
                }
            }
        }

继续来查看下setTaskFromIntentActivity这个方法

private void setTaskFromIntentActivity(ActivityRecord intentActivity) {

        if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
        
            //如果是 FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TASK
            final TaskRecord task = intentActivity.getTask();
            //清空task
            task.performClearTaskLocked();
            mReuseTask = task;
            mReuseTask.setIntent(mStartActivity);
        } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {

            //如果是 SingleInstance 或者 SingleTask 走这里,清空栈内要启动的Activity之前的所有Activity们。
            ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
                    mLaunchFlags);
            //如果top == null 继续走,不为null,就结束了这个方法
            if (top == null) {
                ... ... ...
            }
        } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
            // 判断是否是 SingleTop 模式
            // 这种情况如何复现? SingleTop + FLAG_ACTIVITY_NEW_TASK + taskAffinity。
            // FLAG_ACTIVITY_NEW_TASK + taskAffinity去指定一个特定存在的栈,且栈顶是我们要启动的singleTop模式的activity
            if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                        || LAUNCH_SINGLE_TOP == mLaunchMode)
                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
                if (intentActivity.frontOfTask) {
                    intentActivity.getTask().setIntent(mStartActivity);
                }
                deliverNewIntent(intentActivity);
            } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
                ... ... ...
            }
        } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
            ... ... ...
        } else if (!intentActivity.getTask().rootWasReset) {
            ... ... ...
        }
    }

前提是reusedActivity不为null,看两种情况:
1.如果是SingleTask、SingleInstance模式的Activity,则调用performClearTaskLocked方法,把要启动的Activity之前的所有Activity都清除掉。
2.reusedActivity的启动模式恰好是SingleTop,且也是我们需要启动的Activity,执行 deliverNewIntent。
上述的两种情况能够满足下面的if判断 !mAddingToTask && mReuseTask == null ,然后return结束。

判断SingleTop模式

继续看 startActivityUnchecked 后面的代码,这一部分是针对SingleTop模式的处理。

        ... ... ...
        //下面这段英文解释的很好(SingleTop模式),当我们要启动的Actiivty恰好是当前栈顶的Activity,检查是否只需要被启动一次
        // If the activity being launched is the same as the one currently at the top, then
        // we need to check if it should only be launched once.
        final 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));  
        // SINGLE_TOP  SINGLE_TASK,要启动的Activiy恰好在栈顶
        // dontStart为true,表示不会去启动新的Activity,复用栈顶的Activity
        if (dontStart) {
            // For paranoia, make sure we have correctly resumed the top activity.
            topStack.mLastPausedActivity = null;
            if (mDoResume) {
                // resume  Activity
                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;
        }
栈的复用和新建

继续分析startActivityUnChecked方法的最后一部分,这部分主要是有关于栈是否需要新建。

    ... ... ...
        //必要条件是有FLAG_ACTIVITY_NEW_TASK
        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
            //要建立新栈或者使用已经存在的栈,FLAG_ACTIVITY_NEW_TASK是必要条件
            newTask = true;
            result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
        } else if (mSourceRecord != null) {
            //把被启动的mStartActivity放在启动者mSourceRecord所在的栈上
            result = setTaskFromSourceRecord();
        }
     ... ... ...

setTaskFromReuseOrCreateNewTask 方法

private int setTaskFromReuseOrCreateNewTask(
            TaskRecord taskToAffiliate, ActivityStack topStack) {
            
        mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);

        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);

        } else {
            //用旧栈
            addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
        }
        ... ... ...
总结
  1. 新建栈或者复用栈的必要条件是FLAG_ACTIVITY_NEW_TASK。SingleTask,SingleInstance会为mLaunchFlags自动添加FLAG_ACTIVITY_NEW_TASK。也就是说他们都有存在不使用当前栈的可能。
  2. 新建栈或者复用已经存在栈的充分必要条件是什么?
    1. FLAG_ACTIVITY_NEW_TASK + taskAffinity(taskAffinity必须与当前显示的栈的rootAffinity不相同,taskAffinity默认是包名)。
    2. FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_MULTIPLE_TASK 这是必定会新建一个栈的。
  3. SingleTask可以理解成FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP + (taskAffinity == 该应用包名)。补充一下,FLAG_ACTIVITY_CLEAR_TOP的作用是啥?比如我们要启动的SingleTask模式的Activity已经在栈内,且不在栈的头部,会把之前的Activity全部出栈。
  4. SingleInstance比较特殊,首先SingleInstance模式的Activity会被加上FLAG_ACTIVITY_NEW_TASK,这一点和SingleTask一样。特殊的地方其一在于如果启动过了,会去遍历找相等的Activity,查找过程不一样。而不像SingleTask是去找"合适的"栈,根据taskAffinity来查找。其二在于SingleInstance一个栈只能存放一个Activity,能做到这个的原因是我们在根据taskAffinity找到合适的栈的时候,如果发现是SingleInstance模式Activity的栈,直接忽略。

最后

我整理了一套Android面试题合集,除了以上面试题,还包含【Java 基础、集合、多线程、虚拟机、反射、泛型、并发编程、Android四大组件、异步任务和消息机制、UI绘制、性能调优、SDN、第三方框架、设计模式、Kotlin、计算机网络、系统启动流程、Dart、Flutter、算法和数据结构、NDK、H.264、H.265.音频编解码、FFmpeg、OpenMax、OpenCV、OpenGL ES
在这里插入图片描述

有需要的朋友可以扫描下方二维码,免费领取全部面试题+答案解析!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值