'In Activity.onCreate(), why does Intent.getExtras() sometimes return null?

This was probably a false alarm, see my own answer. Original question below:

An activity has a button that takes the user to another activity. To launch the new activity, we populate our Intent with extras, and onCreate(), the new activity reads from those extras via Intent.getExtras(). We assumed the returned bundle would be non-null, but as customer crash reports discovered, getExtras() sometimes returns null.

Null-guarding the extras, as this answer shows, is perfectly fine, but if you populate the intent's extras, then why would it ever return null later? Is there a better place (like onResume()) to read the extras?

EDIT: It may be because we are not following the name convention required for the keys:

The name must include a package prefix, for example the app com.android.contacts would use names like "com.android.contacts.ShowAll".

This is from the Intent.putExtras javadoc. What happens if you don't follow this name convention; is the behavior even defined?

Here's the relevant code:

class FromActivity extends Activity {

    ImageButton button;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.from_view);

        button = (ImageButton)findViewById(R.id.button);
        button.setVisibility(View.VISIBLE);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(FromActivity.this, ToActivity.class);
                i.putExtra(ToActivity.SERVER_PARAM, ...);
                i.putExtra(ToActivity.UUID_PARAM, ...);
                i.putExtra(ToActivity.TEMPLATE_PARAM, ...);
                startActivityForResult(i, 0); 
                overrideTransition(R.anim.slide_left_in, R.anim.slide_left_out);
            }
        });
    }

}

class ToActivity extends Activity {

    public static final String SERVER_PARAM = "server";
    public static final String UUID_PARAM = "uuid";
    public static final String TEMPLATE_PARAM = "template";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle extras = getIntent().getExtras();
        if (extras == null) {
            finish();
            return;
        }

        // do stuff with extras
    }
}

Here is a sample stack trace of this problem:

java.lang.RuntimeException: Unable to start activity ComponentInfo{ToActivity}: java.lang.NullPointerException
    at  android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2355)
    at  android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
    at  android.app.ActivityThread.access$600(ActivityThread.java:151)
    at  android.app.ActivityThread$H.handleMessage(ActivityThread.java:1335)
    at  android.os.Handler.dispatchMessage(Handler.java:99)
    at  android.os.Looper.loop(Looper.java:155)
    at  android.app.ActivityThread.main(ActivityThread.java:5493)
    at  java.lang.reflect.Method.invokeNative(Native Method)
    at  java.lang.reflect.Method.invoke(Method.java:511)
    at  com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
    at  com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
    at  dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NullPointerException
    at  ToActivity.onCreate(SourceFile:49)
    at  android.app.Activity.performCreate(Activity.java:5066)
    at  android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1101)
    at  android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2311)
     ... 11 more
     java.lang.NullPointerException
    at  ToActivity.onCreate(SourceFile:49)
    at  android.app.Activity.performCreate(Activity.java:5066)
    at  android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1101)
    at  android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2311)
    at  android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
    at  android.app.ActivityThread.access$600(ActivityThread.java:151)
    at  android.app.ActivityThread$H.handleMessage(ActivityThread.java:1335)
    at  android.os.Handler.dispatchMessage(Handler.java:99)
    at  android.os.Looper.loop(Looper.java:155)
    at  android.app.ActivityThread.main(ActivityThread.java:5493)
    at  java.lang.reflect.Method.invokeNative(Native Method)
    at  java.lang.reflect.Method.invoke(Method.java:511)
    at  com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
    at  com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
    at  dalvik.system.NativeStart.main(Native Method)


Solution 1:[1]

This was probably a false alarm; it's more likely due to our own instance variable being null:

class ToActivity extends Activity {

    public static final String SERVER_PARAM = "server";
    public static final String UUID_PARAM = "uuid";
    public static final String TEMPLATE_PARAM = "template";

    private State state;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        state = initializeState();
        // MISSING NULL CHECK HERE!

        Bundle extras = getIntent().getExtras();
        if (extras == null) {
            finish();
            return;
        }

        // do stuff with state and extras
    }
}

Solution 2:[2]

You can get it like this:

getIntent().getStringExtra(key);

Or:

getIntent().getExtras().getString(key)

And set it like this in "FromActivity":

Bundle extras = new Bundle();
extras.putString(key, value);
intent.putExtras(extras);
//And start your activity...

Or:

intent.putExtra(key, string);
//And start your activity...

Either way should work, Hope this helps...

Regards!

Solution 3:[3]

theoretically, the Intent that starts your activity could come from anywhere, e.g. another program

Solution 4:[4]

You got a null pointer error, did you miss some initialization? And can you tell us which line of your code has this error. BTW, if you have many activities to handle, set a flag for your intent is a good idea.

Solution 5:[5]

This could be the problem: the user started your activity using the Recent Tasks UI soon after rebooting their device.

I had a similar problem in this code:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent authIntent = getIntent().getParcelableExtra(EXTRA_AUTH_INTENT);
    // Sometimes (rarely) authIntent is null
}

This is in a non-exported Activity that is launched from one place only with the following code, notice there is no way authIntent can be null when the activity is started!

    if (authIntent == null) {
        throw new IllegalStateException();
    }
    Intent intermediaryIntent = new Intent(context, MyIntermediaryActivity.class);
    intermediaryIntent.putExtra(EXTRA_AUTH_INTENT, authIntent);
    intermediaryIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intermediaryIntent);

But if you do what I described, launch the activity normally, reboot your device and use Recent Tasks UI to start it again you will see the extras are missing. This feels like a bug in Android. Reproduced on Android 10 but probably happens on many other versions.

The fix I guess is either exclude from recents via android:excludeFromRecents in the manifest or if you unexpectedly get null on an extra just call finish().

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ten-Young Guh
Solution 2
Solution 3 tallen
Solution 4 Spark8006
Solution 5 satur9nine