22

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)
Community
  • 1
  • 1
Ten-Young Guh
  • 255
  • 1
  • 2
  • 9
  • is this your complete code You are missing `setContetnView` . and post stacktrace. – Raghunandan Aug 09 '13 at 18:58
  • No, it's not the complete code, just the relevant stuff. – Ten-Young Guh Aug 09 '13 at 19:00
  • 2
    You haven't added any `extras` from what I see – codeMagic Aug 09 '13 at 19:00
  • 1
    Can you see what devices the crash reports are coming in on? I've also had a similar problem with `getAction()` returning null on some customer devices, where the only way to start the activity is via an action that matches an intent filter. There's definitely some weird stuff going on "out in the wild"... – Alex MDC Aug 09 '13 at 19:00
  • If you wont put extras (as bundle explicitly to Intent) it will return null, obviously how codeMagic mentioned. – Simon Dorociak Aug 09 '13 at 19:07
  • I attached the stack trace. Unfortunately since this code is from an employer I can't just paste the exact code in. – Ten-Young Guh Aug 09 '13 at 19:07
  • The comment "// call i.putExtra()" was supposed to mean extras are being added. I'll edit the code to clarify that. – Ten-Young Guh Aug 09 '13 at 19:09
  • 1
    Maybe is not a "false alarm", I have got 9 crash reports about this NPE, and finger out that maybe there is something wrong with android 7.1, cuz all of the 9 crashes were happened on this OS version. so i bet that your crashes on the reports were also happened on this OS version, look forward for your reply, thanks. – wuzhanning May 29 '18 at 09:39

5 Answers5

18

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!

Martin Cazares
  • 13,637
  • 10
  • 47
  • 54
  • 1
    But why would, for example, i.putExtra(TITLE_PARAM, "Title") then extras.getString(TITLE_PARAM) work (almost) all the time then? – Ten-Young Guh Aug 09 '13 at 19:27
  • Because you are directly looking for the "extra" in the intent it self, not in the bundle, in ToActivity you are trying to get the "Bundle" object out of the intent, but you never set a bundle, you went directly for the extras... – Martin Cazares Aug 09 '13 at 19:29
  • 2
    It did work. The weird thing however is that the "wrong" way also works 99% of the time as well; AFAIK we have literally never been able to reproduce any crashes caused by the wrong way. The only crashes from this problem we know of are from customer reports. – Ten-Young Guh Aug 09 '13 at 19:37
  • 1
    The [Android javadoc](http://developer.android.com/reference/android/content/Intent.html#getExtras%28%29) says getExtras() returns "the map of all extras previously added with putExtra()," so why would getStringExtra and extras.getString be any different? – Ten-Young Guh Aug 09 '13 at 19:44
  • As [Intent's source code](http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2.1_r1/android/content/Intent.java) (2.2.1_r1) shows, getExtras returns a copy of the same bundle that getStringExtra and friends use, unless this behavior was changed since then? – Ten-Young Guh Aug 09 '13 at 20:01
  • I would lie to you if i tell you something have changed, since i haven't go really deep in the source code, however i'm sure i've had the same issue and the answer i gave you did the trick and never heard of it again... – Martin Cazares Aug 09 '13 at 20:30
1

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

tallen
  • 677
  • 6
  • 17
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
    }
}
Ten-Young Guh
  • 255
  • 1
  • 2
  • 9
  • 1
    What do you mean by 'false alarm'? The stacktrace terminates in the constructor and there are no instance var references there. I don't get how this can be the accepted answer. What am I missing?...thx – Rondo Nov 13 '13 at 05:34
  • The problem was there *was* an instance variable; I (wrongly) thought it was irrelevant and left it out in my original question. It's my own answer; hence the accepted answer. – Ten-Young Guh Nov 14 '13 at 06:15
0

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.

Spark8006
  • 635
  • 1
  • 7
  • 15
0

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().

satur9nine
  • 13,927
  • 5
  • 80
  • 123