个人博客

http://www.milovetingting.cn

Application中以标准模式启动Activity报错的原因分析

Android中,启动的Activity都会运行在相应的任务栈中。如果直接在Application中以标准模式启动Activity,则会报出以下错误(Android7、Android8除外,后面会分析):

1
2
3
4
Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
at android.app.ContextImpl.startActivity(ContextImpl.java:912)
at android.app.ContextImpl.startActivity(ContextImpl.java:888)
at android.content.ContextWrapper.startActivity(ContextWrapper.java:379)

错误信息提示需要FLAG_ACTIVITY_NEW_TASK的Flag.

在Activity中调用getApplication()或者getApplicationContext(),最终获取到的都是Application,那么getApplication().startActivity()或者getApplicationContext().startActivity()方法都在Application中

1
2
3
4
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}

在Application的startActivity方法里,又通过mBase来调用startActivity,而mBase就是ContextImpl。ContextImpl的startActivity方法

1
2
3
4
5
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}

这个方法里调用了另一个重载方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Android6
@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?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}

Android6中,在这个方法里,看到了前面抛出的异常提示。在这里判断了,如果没有加FLAG_ACTIVITY_NEW_TASK的Flag,就会抛出异常。

再来看Android7中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();

// 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.
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& 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);
}

可以看到,在Android7中,相对Android6,抛出异常还需要符合其它的几个条件。而从前面Application的startActivity方法来看,Bundle类型的options参数为null,所以这个条件不成立,不会抛出异常。

再来看Android8中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();

// 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.
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& 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);
}

和Android7一样,也判断了options,由于options为null,因此条件不成立,也不会抛出异常。

再来看下Android9中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();

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

在Android9中,对于options==null用了||判断,那么ActivityOptions.fromBundle(options).getLaunchTaskId() == -1,条件还是会成立,还是会抛出异常。

可以看到,在Android7和Android8中,可以在Application中直接启动Activity,而不需要增加FLAG_ACTIVITY_NEW_TASK的Flag。