IllegalStateException: активность была уничтожена - когда приложение пытается снова показать DialogFragment

Я нашел много похожих вопросов в stackoverflow, но их решения не работают в моем случае. Они связаны с методом onSaveInstanceState и особенно с поддержкой библиотеки поддержки.

У меня есть Activity, который запускает на одном событии этот код:

MyDialogFragment.showMyDialog(name, this, this);
Параметр

name - это объект String. Второй параметр (this) - это просто объект класса Activity, а третий (также this) - это простой интерфейс. Этот мой Activity реализует этот интерфейс. showMyDialog() является, конечно, статическим методом. Это тело:

MyDialogFragment fragment = new MyDialogFragment(listener, "Hello " + name);
fragment.show(activity.getFragmentManager(), "myDialog");

Это работает с первой попытки. Но на втором я получаю это исключение:

E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.myapp, PID: 20759
java.lang.IllegalStateException: Activity has been destroyed
        at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1345)
        at android.app.BackStackRecord.commitInternal(BackStackRecord.java:597)
        at android.app.BackStackRecord.commit(BackStackRecord.java:575)
        at android.app.DialogFragment.show(DialogFragment.java:230)
        at com.example.myapp.view.dialog.MyDialogFragment.showMyDialog(MyDialogFragment.java:41)
        at com.example.myapp.MyActivity.showMyDialog(MyActivity.java:208)
        at com.example.myapp.MyActivity.onEvent(MyActivity.java:232)
        at com.example.myapp.MyActivity.handleMessage(MyActivity.java:89)
        at android.os.Handler.dispatchMessage(Handler.java:98)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5017)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
        at dalvik.system.NativeStart.main(Native Method)

В общем, это шаги для воспроизведения моего исключения:

  • Введите Activity - получено событие, поэтому отображается MyDialogFragment.
  • Отклонить диалог с помощью отрицательной кнопки.
  • Введите Activity еще раз - MyDialogFragment снова отображается.
  • Введите код в EditText.
  • В этот момент валидация занимает некоторое время. Соединение с сервером и т.д. И возвращается отрицательный результат. Поэтому нужно еще раз показать MyDialogFragment. Но в этот момент я получаю исключение.

Однако, когда я пропускаю вторую точку и начинаю вводить неверный код для проверки, MyDialogFragment будет отображаться без каких-либо проблем. Странное поведение.

Я попытался использовать нестатический метод setRetainInstance(true), а также commitAllowingStateLoss. Но не было никакой разницы.

Ответ 1

Это немного странно - или, может быть, просто ошибка в этой функциональности Android. Я добавил блок try catch, чтобы поймать исключение броска таким образом:

MyDialogFragment fragment = new MyDialogFragment(listener, "Hello " + name);
try {
    fragment.show(activity.getFragmentManager(), "myDialog");
} catch (Exception e) {
    e.printStackTrace();
}

И, конечно же, исключение все еще бросает (и ловит в этот момент), и что интересно, мой фрагмент диалога воссоздается корректно, и пользователь может взаимодействовать с ним.

Ответ 2

В методе showMyDialog() сначала попробуйте:

MyDialogFragment fragment = activity.findFragmentByTag("myDialog");

if (fragment == null), затем выполните инициализацию:

MyDialogFragment fragment = new MyDialogFragment(listener, "Hello " + name);

else показать это:

fragment.show(activity.getFragmentManager(), "myDialog");

ОБНОВЛЕНИЕ:

void showMyDialog() {
    MyDialogFragment fragment = activity.getFragmentManager().findFragmentByTag("myDialog");
    if (fragment == null) {
        fragment = new MyDialogFragment(listener, "Hello " + name);
    }
    fragment.show(activity.getFragmentManager(), "myDialog");
}

Ответ 3

(для API 17 и выше)

В случае, если у кого-либо еще есть эта проблема (или даже связанная с методом транзакции commit()), я просто добавил перед ней проверку:

private showFragment() {
    if (isDestroyed()) {
        return;
    }

    // Else do whatever transaction...
}

Ответ 4

Просто измените нижнюю строку

MyDialogFragment.showMyDialog(name, this, this);

to

MyDialogFragment.showMyDialog(name, **"Activity class name.this"**, this);

Ответ 5

У меня была такая же ошибка, когда я пытался добавить SupportMapFragment в DialogFragment. Я могу исправить эту ошибку, используя getChildFramentManager() вместо getFragmentManager(), как это было предложено API при использовании вложенных фрагментов:

public class GpsDialogFragment extends DialogFragment {
    private SupportMapFragment mapFragment;
    private static final String MAP_FRAGMENT_TAG = "MAP_FRAGMENT";


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.gps_postition_dialog, container, false);
        // ...

        // dynamically create the map fragment (https://stackoverflow.com/info/14083950)
        FragmentManager fragmentManager = getChildFragmentManager();
        mapFragment = (SupportMapFragment) fragmentManager.findFragmentByTag(MAP_FRAGMENT_TAG);
        if (mapFragment == null) {
            if (!getActivity().isFinishing()) {
                mapFragment = new SupportMapFragment();
                FragmentTransaction ft = fragmentManager.beginTransaction();
                ft.add(R.id.mapFrame, mapFragment, MAP_FRAGMENT_TAG);
                ft.commit();
                fragmentManager.executePendingTransactions();
            }
        }

        return view;
    }

    // ...

    @Override
    public void onPause() {
        if (mapFragment != null)
            getChildFragmentManager().beginTransaction().remove(mapFragment).commit();
        super.onPause();
    }
}