Android面试官装逼失败之:关于Activity的启动模式

ActivityRecord sourceRecord, int startFlags, boolean doResume,
Bundle options) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;

int launchFlags = intent.getFlags();

// 如果sourceRecord ==null 说明不是从activity启动的,从服务开启就一定要start a new task
if (sourceRecord == null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
} else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}

if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
sendActivityResultLocked(-1,
r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
r.resultTo = null;
}
boolean addingToTask = false;
boolean movedHome = false;
TaskRecord reuseTask = null;

if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
if (r.resultTo == null) {

ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
?findTaskLocked(intent, r.info)
: findActivityLocked(intent, r.info);

if (taskTop != null) {
ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);

if (curTop != null && curTop.task != taskTop.task) {
r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
boolean callerAtFront = sourceRecord == null
|| curTop.task == sourceRecord.task;
if (callerAtFront) {
movedHome = true;
moveHomeToFrontFromLaunchLocked(launchFlags);
moveTaskToFrontLocked(taskTop.task, r, options);
options = null;
}
}

if ((launchFlags &
(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
== (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
reuseTask = taskTop.task;
performClearTaskLocked(taskTop.task.taskId);
reuseTask.setIntent(r.intent, r.info);

} else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {

ActivityRecord top = performClearTaskLocked(
taskTop.task.taskId, r, launchFlags);
if (top != null) {

if (top.frontOfTask) {
top.task.setIntent(r.intent, r.info);
}
top.deliverNewIntentLocked(callingUid, r.intent);
} else {

addingToTask = true;
sourceRecord = taskTop;
}
} else if (r.realActivity.equals(taskTop.task.realActivity)) {

if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
&& taskTop.realActivity.equals(r.realActivity)) {
if (taskTop.frontOfTask) {
taskTop.task.setIntent(r.intent, r.info);
}
taskTop.deliverNewIntentLocked(callingUid, r.intent);

} else if (!r.intent.filterEquals(taskTop.task.intent)) {
addingToTask = true;
sourceRecord = taskTop;
}
}

if (!addingToTask && reuseTask == null) {
if (doResume) {
resumeTopActivityLocked(null, options);
} else {
ActivityOptions.abort(options);
}
return ActivityManager.START_TASK_TO_FRONT;
}
}
}
}

if (r.packageName != null) {
ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
if (top.app != null && top.app.thread != null) {

if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
if (doResume) {
resumeTopActivityLocked(null);
}
ActivityOptions.abort(options);
if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
return ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
top.deliverNewIntentLocked(callingUid, r.intent);
return ActivityManager.START_DELIVERED_TO_TOP;
}
}
}
}

}

boolean newTask = false;
boolean keepCurTransition = false;

if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {

if (reuseTask == null) {
mService.mCurTask++;
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
} else {

r.setTask(reuseTask, reuseTask, true);
}
newTask = true;
if (!movedHome) {
moveHomeToFrontFromLaunchLocked(launchFlags);
}

} else if (sourceRecord != null) {

if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
ActivityRecord top = performClearTaskLocked(
sourceRecord.task.taskId, r, launchFlags);
keepCurTransition = true;
if (top != null) {
top.deliverNewIntentLocked(callingUid, r.intent);
if (doResume) {
resumeTopActivityLocked(null);
}
ActivityOptions.abort(options);
return ActivityManager.START_DELIVERED_TO_TOP;
}
}

r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
} else {
final int N = mHistory.size();
ActivityRecord prev =
N > 0 ? mHistory.get(N-1) : null;
r.setTask(prev != null
? prev.task
: new TaskRecord(mService.mCurTask, r.info, intent), null, true);
}
startActivityLocked(r, newTask, doResume, keepCurTransition, options);
return ActivityManager.START_SUCCESS;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为什么非Activity启动Activity要强制规定使用参数FLAG_ACTIVITY_NEW_TASK

从源码上说,ContextImpl在前期做了检查,如果没添加Intent.FLAG_ACTIVITY_NEW_TASK就抛出异常,

@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "

  • " context requires the FLAG_ACTIVITY_NEW_TASK flag."
  • " Is this really what you want?");
    }

    }

为什么要这么呢?其实直观很好理解,如果不是在Activity中启动的,那就可以看做不是用户主动的行为,也就说这个界面可能出现在任何APP之上,如果不用Intent.FLAG_ACTIVITY_NEW_TASK将其限制在自己的Task中,那用户可能会认为该Activity是当前可见APP的页面,这是不合理的。举个例子:我们在听音乐,这个时候如果邮件Service突然要打开一个Activity,如果不用Intent.FLAG_ACTIVITY_NEW_TASK做限制,那用户可能认为这个Activity是属于音乐APP的,因为用户点击返回的时候,可能会回到音乐,而不是邮件(如果邮件之前就有界面)。

总结

以上分析只是针对一个版本的Android,并且只涉及部分Flag,要完全理解各种组合就更麻烦了,所以所,如果面试官问题Activity启动模式的话,随便侃侃还可以,但是要以此来鄙视你,那你有90%的概率可以怼回去,怎么怼?随便几个Flag组合一下,问面试官会有什么结果,保证问死一堆 ;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

####福利时间:

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以加一下下面的技术群来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后

分享一份NDK基础开发资料

详解:Linux网络虚拟化技术

分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
6)]

分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值