Невозможный NPE во фрагменте

#android #android-fragments

#Android #android-фрагменты

Вопрос:

Fabric рассказал мне о нескольких случаях NPE, которые я не могу объяснить. Они происходят на разных устройствах и версиях Android.

 Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{lelisoft.com.lelimath/lelisoft.com.lelimath.activities.CalcActivity}: java.lang.NullPointerException
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2328)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2386)
   at android.app.ActivityThread.access$900(ActivityThread.java:169)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1277)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:136)
   at android.app.ActivityThread.main(ActivityThread.java:5476)
   at java.lang.reflect.Method.invokeNative(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:515)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
   at dalvik.system.NativeStart.main(NativeStart.java)
Caused by java.lang.NullPointerException
   at lelisoft.com.lelimath.fragment.CalcFragment.setupPlay(CalcFragment.java:253)
   at lelisoft.com.lelimath.fragment.CalcFragment.onActivityCreated(CalcFragment.java:86)
   at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:2089)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1133)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1290)
   at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1272)
   at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:2149)
   at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
   at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:600)
   at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
   at lelisoft.com.lelimath.activities.LeliBaseActivity.onStart(LeliBaseActivity.java:94)
   at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1177)
   at android.app.Activity.performStart(Activity.java:5461)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2301)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2386)
   at android.app.ActivityThread.access$900(ActivityThread.java:169)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1277)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:136)
   at android.app.ActivityThread.main(ActivityThread.java:5476)
   at java.lang.reflect.Method.invokeNative(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:515)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
   at dalvik.system.NativeStart.main(NativeStart.java)
  

А теперь исходный код для действия, которое всегда инициализирует экземпляр GameLogic и передает его фрагменту, уже находящемуся в onCreate():

 public class CalcActivity .. {
 GameLogic gameLogic;

 protected void onCreate(Bundle state) {
    setContentView(R.layout.act_calc);
    setGameLogic(new CalcLogicImpl());
    calcFragment = new CalcFragment();
    calcFragment.setLogic((CalcLogic) gameLogic);
    initializeCalcFragment(false);
 }

private void initializeCalcFragment(boolean replace) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    if (replace) {
        transaction.replace(R.id.calc_content, calcFragment);
    } else {
        transaction.add(R.id.calc_content, calcFragment);
    }
    transaction.commit();
}

public void setGameLogic(GameLogic gameLogic) {
    this.gameLogic = gameLogic;
}
  

Затем происходит вычисление фрагмента:

 public class CalcFragment extends LeliBaseFragment {
 CalcLogic logic;

 public void setLogic(CalcLogic logic) {
    this.logic = logic;
 }

 public void onActivityCreated(Bundle state) {
    setupPlay();
 }
  

и здесь он терпит неудачу с NPE:

 private void setupPlay() {
    formulas = logic.generateFormulas();
}
  

Поток понятен, и другого назначения свойств нет. Создается действие, создается и устанавливается свойство, создается фрагмент и передается свойство. Затем фрагмент инициализируется, onActivityCreated но иногда свойство равно null. Он отлично работает на всех моих реальных и виртуальных устройствах. Я понятия не имею, почему он пуст на этих сообщаемых устройствах.

Комментарии:

1. Где вы инициализируете переменную GameLogic?

2. в setter setGameLogic см. onCreate

3. Что такое LeliBaseActivity.onStart() ? Вот где активируется ваш фрагмент.

4. Какая строка вызывает NPE?

5. NPE — это formulas = logic.generateFormulas();

Ответ №1:

Фреймворку Android часто требуется переустанавливать фрагменты, например, для обработки изменения конфигурации. Состояние экземпляра, такое как ваше CalcLogic , потеряно.

Некоторые варианты его исправления:

  1. Извлеките значение для logic поля из действия в фрагменте onCreate() или другом подобном методе раннего жизненного цикла.

  2. Вызов setRetainInstance(true) , чтобы экземпляр фрагмента сохранился в некоторых случаях, когда обычно происходит переустановка.

Комментарии:

1. Этого не произошло с тех пор, как я переписал эту часть. Спасибо.