Почему в Activity.onCreate() Intent.getExtras() иногда возвращает null?

Вероятно, это была ложная тревога, см. Мой собственный ответ. Оригинальный вопрос ниже:

У действия есть кнопка, которая переводит пользователя в другое действие. Чтобы запустить новое действие, мы заполняем наше намерение дополнениями, а onCreate(), новое действие считывает эти дополнения через Intent.getExtras(). Мы предполагали, что возвращенный пакет будет ненулевым, но, как обнаружены отчеты о сбоях клиентов, getExtras() иногда возвращает ноль.

Как показывает этот ответ, нулевая охрана дополнительных функций - это совершенно нормально, но если вы заполняете дополнительные функции, то почему они возвращаются нулевыми позже? Есть ли лучшее место (например, onResume()) для чтения дополнений?

РЕДАКТИРОВАТЬ: Это может быть потому, что мы не следуем соглашению имен, необходимых для ключей:

Имя должно содержать префикс пакета, например, приложение com.android.contacts будет использовать такие имена, как "com.android.contacts.ShowAll".

Это из Javadoc Intent.putExtras. Что произойдет, если вы не будете следовать этому соглашению имен; поведение даже определено?

Вот соответствующий код:

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
    }
}

Вот пример трассировки стека этой проблемы:

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)

5 ответов

Решение

Это была, вероятно, ложная тревога; это более вероятно из-за нашей собственной переменной экземпляра, являющейся нулем:

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
    }
}

Вы можете получить это так:

getIntent().getStringExtra(key);

Или же:

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

И установите это так в "FromActivity":

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

Или же:

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

В любом случае должно работать, надеюсь, это поможет...

С уважением!

Теоретически, Намерение, которое начинает вашу деятельность, может прийти откуда угодно, например, из другой программы.

Это может быть проблемой: пользователь начал вашу деятельность с помощью пользовательского интерфейса недавних задач вскоре после перезагрузки своего устройства.

У меня была аналогичная проблема в этом коде:

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

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

Это неэкспортируемая активность, которая запускается из одного места только со следующим кодом, обратите внимание, что нет способа authIntentможет быть нулевым, когда активность запущена!

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

Но если вы сделаете то, что я описал, запустите действие в обычном режиме, перезагрузите устройство и используйте пользовательский интерфейс «Недавние задачи», чтобы запустить его снова, вы увидите, что дополнительные функции отсутствуют. Это похоже на ошибку в Android. Воспроизведено на Android 10, но, вероятно, происходит и во многих других версиях.

Я предполагаю, что исправление заключается в том, чтобы исключить из недавних через android:excludeFromRecentsв манифесте или если вы неожиданно получили null при дополнительном простом вызове finish().

Вы получили ошибку нулевого указателя, вы пропустили инициализацию? И вы можете сказать нам, какая строка вашего кода имеет эту ошибку. Кстати, если у вас есть много действий для выполнения, установить флаг для вашего намерения это хорошая идея.

Другие вопросы по тегам