Фрагмент должен быть общедоступным статическим классом, который должен быть правильно воссоздан из состояния экземпляра

После обновления до последнего репозитория поддержки

compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:design:24.2.0'
compile 'com.android.support:percent:24.2.0'
compile 'com.android.support:recyclerview-v7:24.2.0'

Я получаю странное исключение.

java.lang.IllegalStateException: Fragment null must be a public static class to be  properly recreated from instance state.
at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:435)
at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:414)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:154)
at com.androidapp.base.BaseActivity.showDialogFragment(BaseActivity.java:78)
at com.androidapp.MainActivity.showNewDialog(MainActivity.java:304)
at com.androidapp.MainActivity$6.onClick(MainActivity.java:228)

В моем классе BaseActivity я создал повторно используемый фрагмент, который можно использовать в классе активности, который расширяет BaseActivty

public void showDialogFragment(DialogFragment newFragment) {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
        if (prev != null) {
            ft.remove(prev);
        }
        ft.addToBackStack("dialog");
        newFragment.show(ft, "dialog");
    }

Вернемся к MainActivty. Я использовал фрагмент, подобный этому,

public class MainActivity extends BaseActivity {

    @SuppressLint("ValidFragment")
        public void showNewDialog(int type, String title, String message) {
            final DialogNew dialog = new DialogNew() {
                @Override
                public void success(boolean isLandscape) {
                    .......
                }

                @Override
                public void cancel() {

                }
            };
            dialog.setArgs(title, message);
            super.showDialogFragment(dialog);
        }
}

Ниже приведен класс DialogNew,

public abstract class DialogNew extends DialogFragment {

    private View rootView;

    private String title;
    private String message;

    public void setArgs(String title, String message) {
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("message", message);
        setArguments(args);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_TITLE, 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        rootView = inflater.inflate(R.layout.fragment_new_dialog, container, false);

        init();
        setListeners();

        return rootView;
    }

    public abstract void success(boolean isLandscape);

    public abstract void cancel();
}

PS: тот же код работает со старым репозиторием поддержки.

Ответ 1

Ошибка не особенно странная. Если вы раньше не получали эту ошибку, это было странно.

Android уничтожает и воссоздает фрагменты как часть изменения конфигурации (например, поворот экрана) и как часть перестройки задачи, если это необходимо (например, пользователь переключается на другое приложение, ваш процесс приложения завершается, когда он находится в фоновом режиме, то пользователь пытается вернуться в ваше приложение, все в течение 30 минут или около того). Android не имеет возможности воссоздать анонимный подкласс DialogNew.

Итак, создайте обычный public класс Java (или public static вложенный класс), который расширяет DialogNew и имеет вашу бизнес-логику, заменив анонимный подкласс DialogNew, который вы используете в настоящее время.

Ответ 2

Изменить: Вы, вероятно, не хотите этого делать... См. комментарии.

Пример кода похож на то, что я предложил в здесь, а также недавно обнаружил, что решение, которое у меня там было, больше не работает. Я обновил свой ответ там для Java7, но если у вас Java8, решение очень просто:

(Я еще не тестировал это)

public class DialogNew extends DialogFragment {
    private View rootView;
    private String title;
    private String message;

    // Do nothing by default
    private Consumer mSuccess = (boolean b) -> {};
    private Runnable mCancel = () -> {};

    public void setArgs(String title, String message) {
        Bundle args = new Bundle();
        args.putString("title", title);
        args.putString("message", message);
        setArguments(args);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NO_TITLE, 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_new_dialog, container, false);
        // use mSuccess.accept(boolean) when needed
        init();
        setListeners();
        return rootView;
    }

    public void setSuccess(Consumer success) {
        mSuccess = success;
    }

    public void setCancel(Runnable cancel) {
        mCancel = cancel;
    }
}

Затем в основной деятельности:

public class MainActivity extends BaseActivity {
        public void showNewDialog(int type, String title, String message) {
            final DialogNew dialog = new DialogNew();
            dialog.setArgs(title, message);
            dialog.setSuccess((boolean isLandscape) -> {
                //....
            });
            super.showDialogFragment(dialog);
        }
}