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

个人博客

http://www.milovetingting.cn

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

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

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中

@Override
    public void startActivity(Intent intent) {
        mBase.startActivity(intent);
    }

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

@Override
    public void startActivity(Intent intent) {
        warnIfCallingFromSystemProcess();
        startActivity(intent, null);
    }

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

//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中的方法

@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中的方法

 @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中的方法

@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。

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

上一篇:Parsing error: 'import' and 'export' may only appear at the top level


下一篇:C盘文件移动与mklink