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


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

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

MyDialogFragment.showMyDialog(name, this, this);

name параметром является объект String. Второй параметр (this) - это объект класса just 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)

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

  1. введите Activity - событие получено, так что MyDialogFragment показано.
  2. закрыть диалог с помощью отрицательной кнопки.
  3. введите Activity еще раз - MyDialogFragment будет показано снова.
  4. введите некоторый код в EditText.
  5. в этот момент проверка занимает некоторое время. Подключение с сервером и т. д. И возвращается отрицательный результат. Так что нужно еще раз показать MyDialogFragment. Но в данный момент я получаю исключение.

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

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

5 12

5 ответов:

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

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

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

В методе showMyDialog() первая попытка :

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

Если (fragment == null), то инициализируем :

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

Еще покажи:

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

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

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

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

    // Else do whatever transaction...
}

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

MyDialogFragment.showMyDialog(name, this, this);

К

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

У меня была та же ошибка, когда я пытался добавить 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/questions/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();
    }
}