#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
, потеряно.
Некоторые варианты его исправления:
-
Извлеките значение для
logic
поля из действия в фрагментеonCreate()
или другом подобном методе раннего жизненного цикла. -
Вызов
setRetainInstance(true)
, чтобы экземпляр фрагмента сохранился в некоторых случаях, когда обычно происходит переустановка.
Комментарии:
1. Этого не произошло с тех пор, как я переписал эту часть. Спасибо.