基于 Android 11
Android 源码太过庞杂,每次深入流程看完,隔天就忘了,还是得从细微处出发,由浅入深才行!
由于本文是为了分析两种子类实现 startActivity(…) 的不同,有一些细节就省略不表,专注于不同的地方
/**
* Same as {@link #startActivity(Intent, Bundle)} with no options specified.
* ...
*/
public abstract void startActivity(@RequiresPermission Intent intent);
/**
* Launch a new activity.
* ...
* Note that if this method is being called from outside of an
* {@link android.app.Activity} Context, then the Intent must include
* the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because,
* without being started from an existing Activity, there is no existing
* task in which to place the new activity and thus it needs to be placed
* in its own separate task.
* ...
*/
public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle options);
首先从 startActivity(Intent intent) 开始进入,一路调用都比较正常,没有什么太过值得注意的地方,最后调用了 mInstrumentation.execStartActivity(…) 方法来启动目标 activity
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
...
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// 由于 options 为空,代码走这里
startActivityForResult(intent, -1);
}
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
// mParent 变量用于判断该 activity 是否被镶嵌在另外一个 activity 中
if (mParent == null) {
// 普遍情况
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);
...
} else {
// 镶嵌的情况
...
}
}
在 ContextImpl 类中,startActivity(…) 的调用流程比较短,单参启动双参方法,最后也是调用 mInstrumentation.execStartActivity(…) 方法来启动目标 activity。值得注意的是,在启动目标 activity 之前,方法中做了一个条件判断,不符合条件的话会抛出 AndroidRuntimeException 异常,具体的条件以及设置原因可以通过阅读代码得知,也可以直接看 google 给的注释:通常不允许从没有 FLAG_ACTIVITY_NEW_TASK 启动标志的活动外部(即在非 activity 上下文环境中)调用 startActivity(…) 方法,除非调用者指定了目标启动活动所属的 Task id。在版本 N 和 O-MR1 之间存在一个 bug,这使得它能够在没有指定 FLAG_ACTIVITY_NEW_TASK 的情况下也可以正常启动目标活动。这里做版本判断是为了保持向后的兼容性
@Override
public void startActivity(Intent intent) {
...
startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, Bundle options) {
...
// Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
// generally not allowed, except if the caller specifies the task id the activity should
// be launched in. A bug was existed between N and O-MR1 which allowed this to work. We
// maintain this for backwards compatibility.
final int targetSdkVersion = getApplicationInfo().targetSdkVersion;
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& (targetSdkVersion < Build.VERSION_CODES.N
|| targetSdkVersion >= Build.VERSION_CODES.P)
&& (options == null
|| ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options);
}
源码学习,路漫漫其修远兮,保持本心,好好学习!如有错误,烦请指正!感谢阅读!