Non-Activity environment startActivity correct posture

We know that the non-Activity Context (such as: ApplicationContext) Activity object starts a need to add FLAG_ACTIVITY_NEW_TASKmarks for the job or app will crash and report the following error:

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

Because of the non-Activity Context existing tasks to place the new activity does not exist, it is necessary to place it in its own separate task. As the official document:

Note that if this method is being called from outside of an Activity Context, then the Intent must include the 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.

However, recently I encountered such a period similar to the code in the project:

    mContext.getApplicationContext().startActivity(new Intent(mContext,XXXActivity.class));

Oh excluded, finished! ! ! Not surprisingly, the development of QA colleagues received a red card (bug) warning.

Colleague told me grumbled, his test machine can jump in the past. emmmm ...

Stand in amazement

Instantly thought of these systems tester is not part of version 7.x or 8.x series.

why? Take a look at a wave source

Based on the following without FLAG_ACTIVITY_NEW_TASKanalysis mark

The Context startActivity method is abstract, the method implemented by the class ContextImpl achieved.

  • Look directly Android7.x ~ Android8.x series Source
    @Override
    public void startActivity(Intent intent) {
        warnIfCallingFromSystemProcess();
        startActivity(intent, null);
    }

    @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?");
        }
        ......
    }

These versions are the same source. In general we call only startActivity(Intent intent)methods, in the source code that actually call startActivity(Intent intent, Bundle options)a method, options to a null value, and sets the code determination section has:

        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?");
        }

Focus view options! = Null judgment because options is null, so that the result is false, and the whole set is determined not satisfied, so without adding FLAG_ACTIVITY_NEW_TASKwill not cause abnormal program crashes.

  • Then take a look Android6.x source
    @Override
    public void startActivity(Intent intent, Bundle options) {
        ......
        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?");
        }
        ......
    }

Ah ha ~ ~ ~ no problems, would collapse without ...

That means you can not add android7.0 start FLAG_ACTIVITY_NEW_TASKmark yet?
Do not let the innocent fans blind your eyes

  • Let's look at the source code Android9.x
    @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?");
        }
        ......
    }

It can be seen that, if the targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.Presult is true, i.e., the current system Android7.0 and above 9.0 or less, and options for the null, options == null result is true, and so the entire set is determined true, oh excluded, crashes ! ! !

Tricks and more

If the targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.Presult is false, i.e., the current system Android7.0 to 8.1, so that the whole is determined and set false, escaped unharmed.

Ugh? This is not exactly above Android7.x ~ Android8.x series source logic it? Yes, google did a version compatible.

Sharp eyes of the children's shoes should have seen this note:

     A bug was existed between N and O-MR1 which allowed this to work. We maintain this for backwards compatibility.
     翻译:N和O-MR1版本之间存在一个bug,允许它工作。我们维护这一点是为了向后兼容。

This official document Android9.0 changes have also been mentioned.

In Android 9, you can not start Activity from non-Activity environment unless you pass Intent logo FLAG_ACTIVITY_NEW_TASK. If you try to start Activity in the case of this flag is not passed, then the Activity will not start, the system outputs a message in the log.

Note: Before Android 7.0 (API level 24), marking requirement has been desired behavior and enforcement. A bug in 7.0 Android will temporarily stop the implementation of marking requirements.

Do you think this article is over water? Innocent again lost blind your eyes

Sometimes we also call the direct startActivity(Intent intent, Bundle options)method, passing a non-empty options object, such as adding animated transitions Bundle Activity data sources. Such strips have determined Android7.x ~ Android8.x method:

    options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1

When the options is not null, execute ActivityOptions.fromBundle(options).getLaunchTaskId() == -1judgment

  • See the source code, right ~ ~ ~
    /** @hide */
    public static ActivityOptions fromBundle(Bundle bOptions) {
        return bOptions != null ? new ActivityOptions(bOptions) : null;
    }
  • Then go constructor
public class ActivityOptions {
    ......
    private int mLaunchTaskId = -1;
    ......
    /** @hide */
    public ActivityOptions(Bundle opts) {
        ......
        mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
        ......
    }
    ......
}

Ah ha ~ ~ ~ The question is, under normal circumstances, the options that we live newout, or use the factory to create the structure (such as ActivityOptions.makeCustomAnimation(Context context,int enterResId, int exitResId)), often do not take the initiative to add KEY_LAUNCH_TASK_IDvalue, so mLaunchTaskIdthe default value of -1, then:

    /**
     * @hide
     */
    public int getLaunchTaskId() {
        return mLaunchTaskId;
    }

It returns a value of -1, and the whole resulting set is determined to true, oh excluded, and the program crashes! ! !

This tells you even Android7.x ~ Android8.x system can not be so confident without FLAG_ACTIVITY_NEW_TASKmark, the code is not standardized, two lines of relatives of tears

to sum up

For the following when Android7.0 and 9.0 and above, a non-Activity start Activity environment, plus honestly FLAG_ACTIVITY_NEW_TASKlabeled it; for Android7.0 ~ 8.1 systems, calls startActivity(Intent intent)can not be tagged to call startActivity(Intent intent, Bundle options)upon, options have value you need to pay attention to plus marked or designated as the options added LaunchTaskId.

Final recommendations: For any version of Android, have marked it, respecting google task stack design.

Guess you like

Origin blog.csdn.net/ausboyue/article/details/90648170