Android FragmentManager BackStackRecord.run бросает NullPointerException

Иногда я получаю следующее исключение при работе с фрагментами:

FATAL EXCEPTION: main
java.lang.NullPointerException
    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:591)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1416)
    at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:420)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4745)
    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:786)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    at dalvik.system.NativeStart.main(Native Method)

Исключение возникает, когда run() из BackStackRecord вызывается через execPendingTransactions(), когда он пытается удалить фрагмент из менеджера.

case OP_REMOVE: {
  Fragment f = op.fragment;
  f.mNextAnim = op.exitAnim; <----
  mManager.removeFragment(f, mTransition, mTransitionStyle);
}
break;

Я не могу понять, что именно вызывает это? Я думаю, что это связано с тем, что фрагменты не очищаются сразу после удаления фрагментов.

Ответ 1

Отвечая на мой собственный вопрос:

Это исключение (в конце концов) вызывается при вызове FragmentTransaction.remove(null); и FragmentTransaction.commit();

EDIT: А также, например, Twice Circled и shinyuX указывают в комментарии; при вызове методов show(null) или add(null), attach(null) и detach(null) и, возможно, также hide(null)

После вызова commit() транзакция будет помещена в очередь в FragmentManager. В результате, когда операция обрабатывается после явного вызова FragmentManager.executePendingTransactions() или когда поток очереди FragmentManager вызывает ее, она выдает NullPointerException.

В моем случае я поддерживал состояния фрагментов в глобальном объекте. Там я проверил, показывал ли фрагмент или нет, а затем удалял видимые фрагменты. Но поскольку я начал новую FragmentActivity, эти состояния по-прежнему были установлены в true, пока они не были видны. Так что это ошибка дизайна.

Помимо исправления ошибки проектирования, решение было простым: проверьте, вернул ли FragmentManager.findFragmentByTag() null, прежде чем удалять фрагмент.

Ответ 2

Я не использую тег для создания фрагментов (они работают как контейнеры TabBar).

Итак, он работает при изменении вкладки, но если я нажму кнопку "Назад", я получаю ту же ошибку.

В методе onDestroyView я нашел экземпляр фрагмента с FragmentManager # findFragmentById, однако FragmentManager # findFragmentByTag возвращает null, конечно.

class MyFragment extends ListFragment {

@Override
public void onDestroyView() {
    super.onDestroyView();

    if (this.mapFragment != null
            && getFragmentManager().findFragmentById(
                    this.mapFragment.getId()) != null) {

        getFragmentManager().beginTransaction().remove(this.mapFragment)
                .commit();
        this.mapFragment = null;
    }

}
}