Android 片段必须是公共静态类,才能从实例状态正确地重新创建

Android 片段必须是公共静态类,才能从实例状态正确地重新创建,android,android-fragments,android-support-library,android-dialogfragment,fragmentmanager,Android,Android Fragments,Android Support Library,Android Dialogfragment,Fragmentmanager,更新到最新的支持存储库后 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: Fr

更新到最新的支持存储库后

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类中,我创建了一个可重用的片段,可以在扩展BaseActivity的activity类中使用

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");
    }
回到main活动我使用了这样的片段

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);
        }
}
对话框new类如下所示

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:相同的代码适用于较旧的支持存储库

这个错误并不特别奇怪。如果你以前没有遇到这个错误,那就很奇怪了

Android销毁并重新创建片段,作为配置更改(例如,屏幕旋转)的一部分,以及在需要时重建任务的一部分(例如,用户切换到另一个应用程序,你的应用程序的进程在后台终止,然后用户尝试返回你的应用程序,所有这些都在30分钟左右)。Android无法重新创建DialogNew的匿名子类

因此,创建一个常规的
public
Java类(或
public
嵌套类),该类扩展了
DialogNew
,并具有您的业务逻辑,替换您当前使用的
DialogNew
的匿名子类。

编辑:您可能不想这样做。。。见评论

代码示例看起来与我之前的建议类似,我最近还发现我在那里使用的解决方案不再有效。我已经更新了Java7的答案,但是如果您有Java8,那么解决方案非常简单:

(我还没有测试过这个)

然后在主要活动中:

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

我从头开始重新创建了我的片段,它为我解决了问题


新建->片段->片段(空白),在确认之前取消选中第二个框。

从新建>片段>空白片段创建片段


它对我有用♥♥♥

此错误的原因在指南中有很好的解释

当系统发布配置更改时,它需要能够创建片段的新实例。为了做到这一点,它依赖于片段的默认构造函数,该构造函数不带参数,因此不能有任何依赖项。如果您的片段类不是静态公共类,系统将无法反射地找到此默认构造函数,错误仅表明此情况


为了解决这个问题,您必须重写FragmentManager实例的FragmentFactory的默认实现,该实例将处理片段的创建。我提供的链接中的代码解释了这一点。

为什么
对话框是新的
摘要?不能实例化抽象类。@Vucko没关系。当你做这种事情的时候,是的,你是对的,你不能实例化一个抽象,相反,它会初始化一个扩展这个抽象类的匿名类。简而言之,这没有问题。如果在支持库版本24.2.1中遇到相同的错误,请添加,解决办法是什么,我得到了旧代码,并试图更新支持库,它崩溃的原因是,我们必须做什么?你需要有一个明确定义的公共无参数构造函数,Android不是一个普通的java!谢谢你的回复!我同意你的逻辑。现在我的DialogNew已经扩展了DialogFragment,所以我不能在MainActivity中扩展它,因为MainActivity本身扩展了活动。我不知道如何,但相同的代码在较旧的支持repo上正常工作。我只想把对话和活动类分开。如果你的
DialogFragment
子类是Android库的一部分,你不想在你的公共API中公开它怎么办?@AdamJohns:不幸的是,考虑到Java的工作方式,如果“公共API”定义为“标记为
public
“。在文档中,您可以指出此片段不供库的使用者使用。您甚至可以在库中放置一个定制的Lint检查,以主动警告开发人员,尽管这个过程目前还没有文档记录,并且处于不断变化的状态。但是,从技术角度来看,考虑到它们实现片段的方式,您需要通过
public
zero-argument构造函数来实例化它们。首先,
mSuccess
mCancel
将无法在包裹的碎片中存活。如果保留此片段,则会遇到更糟糕的问题:当调用dialog.setSuccess并向其传递lambda表达式时,实际上已经创建了MainActivity的内部类。当在配置更改上重新创建活动时,将出现内存泄漏,回调将尝试调用已销毁活动的方法。
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);
        }
}