由于 Fragment 类中没有公共构造函数方法,在 android 中旋转设备后出现异常

Posted

技术标签:

【中文标题】由于 Fragment 类中没有公共构造函数方法,在 android 中旋转设备后出现异常【英文标题】:Getting an exception after rotating device in android becuase of absence of public construcotr method in Fragment class 【发布时间】:2021-03-06 11:59:46 【问题描述】:

我用一个包含片段的 Activity 编写了一个非常简单的程序。 我将 Fragment 构造函数设为私有,并且使用 static newInstance() 方法返回片段。 当我旋转手机时,问题就开始了。我收到一个例外,上面写着:

无法启动活动 ComponentInfocom.example.todeleteimmediatley/com.example.MainActivity:androidx.fragment.app.Fragment$InstantiationException:无法实例化片段 com.example.DatesFragment:找不到片段构造函数

我调试了程序,发现异常在onCreate方法的第一行(调用super.onCreate()时)。 谁能解释一下为什么Fragment一定要有构造函数,为什么super.onCreate()阶段会出现异常?

【问题讨论】:

查看***.com/q/51831053/8298909 了解更多信息。重要的一点是,Android 框架需要能够创建 Fragment 的实例,并且它只能*使用公共无参数构造函数来做到这一点。 (*:是的,FragmentFactory 存在,但它可能对大多数人来说太复杂了。) @BenP.,谢谢!我肯定同意你的回答。我很乐意提出一些小问题(如果你认为他们需要一个新帖子,请告诉我,但我真的认为他们不是必需的,因为它们很小并且与原始帖子相关):1。这是否意味着在 onCreate 中我应该在添加和提交之前检查容器是否已经包含 Fragment?因为据我了解,onCreate().super 将恢复旧的 Fragment.2。为什么最好在 newInstance 方法中将 Fragment 需要的信息添加到 Bundle 中,而不是将它们存储为 Fragment 的成员(属性)? 我已经更新了我的答案,希望能解决您的问题。 【参考方案1】:

当您旋转设备时,您的Activity 将被销毁并重新创建。其中一部分是销毁和重新创建您的 Activity 托管的任何 Fragments。

recreation步骤中,Android 框架需要实例化您的Fragment 的一个新实例。默认情况下,它通过调用 Fragment 的无参数构造函数来实现。这意味着这个构造函数必须 (a) 存在并且 (b) 是 public

这种片段的重新创建是由您的 Activity 的super.onCreate() 触发的。

一般建议是创建一个newInstance() 工厂方法(如您所做的那样),但不理会默认构造函数(即不要使其成为private)。是的,这意味着仍然有可能有人直接调用构造函数,这是您不希望的,但如果您不想参与FragmentFactory,则必须这样做。

来自 cmets 的其他问题

这是否意味着在 onCreate 中我应该在添加和提交之前检查容器是否已经包含 Fragment?因为据您了解,onCreate().super 将恢复旧的 Fragment。

我在这里的建议是只提交一次 Fragment 事务,即您的 Activity 第一次启动时。通常,这是通过在提交事务之前检查 savedInstanceState Bundle 是否为空来实现的:

if (savedInstanceState == null) 
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.foo, FooFragment.newInstance(...))
            .commit();

因为您的 Fragments 是您的 Activity 实例状态的一部分,所以任何娱乐(任何时候 savedInstanceState 不是 null)都将自动为您处理。

为什么最好在 newInstance 方法中将 Fragment 需要的信息添加到 Bundle 中而不是将它们存储为 Fragment 的成员(属性)?

一切都回到 Android 框架需要为您的 Fragment 创建一个新实例这一事实。如果您只是在 Fragment 上设置了成员字段,Android 框架将不会意识到这些,并且无法保存或恢复它们。

不过,arguments Bundle 是 Android确实知道的。参数被认为是 Fragment 实例状态的一部分(因此也是包含 Activity 的实例状态的一部分),并将自动保存和恢复。这就是为什么您只能将某些类型的数据放入arguments Bundle; Android 只知道在这个重新创建过程中如何“写入”和“读取”某些类型的数据。

【讨论】:

再次感谢。你写了 replace 方法,可以用 add 方法替换吗?而且我不知道android如何以及哪些类型知道如何阅读,但据我了解,newInstance() 方法无论是娱乐还是新片段都会运行?但我不确定它将使用哪个参数运行,也许这就是问题所在:),因为当我第一次从活动中调用 newInstance 方法时,它不会使用我“设置”的参数运行当我提交“添加”事务时。我认为问题是:“方法 newInstance 将在重新创建阶段运行哪些参数? 大概是您的newInstance() 方法将内容添加到arguments Bundle。您在该 Bundle 中输入的任何值都将自动保存和恢复,因此它将全部“自动”工作。是的,你可以使用add() 而不是replace() 来处理我写的代码。【参考方案2】:

在 Java 中

如果一个类没有定义任何构造函数,那么在编译时,编译器会生成一个没有参数的构造函数(我们通常称其为默认构造函数、无参数构造函数或零参数构造函数)。

如果一个类定义了任何带参数的构造函数,那么在编译时,编译器将不会生成默认构造函数。

谁能解释一下为什么 Fragment 必须有构造函数?

如果你的片段没有接收任何参数,你不需要定义构造函数。

为什么在super.onCreate()阶段出现异常?

在Android中,有几种场景需要系统重新创建一个activity,比如。

配置更改时,例如用户旋转屏幕方向或更改语言。

当系统内存不足时

当重新创建一个activity时,系统会创建一个新的activity实例,然后调用activity生命周期,第一个会是onCreate()回调。在您的活动(例如 MainActivity)中,您需要调用其父活动的super.onCreate()。该语句将恢复活动管理的所有片段(例如您的情况下的 DatesFragment)。

因为这是 MainActivity 的一个新实例,所以它也需要创建一个所有托管 Fragment 的新实例(包括 DatesFragment)。为此,他们将调用默认参数构造函数。

但是在 DatesFragment 中,你将默认构造函数设置为私有,这意味着它只能在该类内部访问,系统无法调用 DatesFragment 类的构造函数,因此会抛出 InstantiationException。

解决方案:

如果你的片段没有接收到任何参数,就不要定义任何构造函数

如果您需要将参数传递给片段,请使用默认构造函数以及setArguments(Bundle)

【讨论】:

感谢您的精彩解释。太糟糕了,我不能批准多个答案,所以我只对你的帖子投了赞成票

以上是关于由于 Fragment 类中没有公共构造函数方法,在 android 中旋转设备后出现异常的主要内容,如果未能解决你的问题,请参考以下文章

类中项目的顺序:字段、属性、构造函数、方法

android关于fragment的构造函数用法建议

Java 类中的哪些变量构造函数可以访问?

子父类中的构造函数

从 javascript 类中的“私有”方法访问“公共”方法

防止 Proguard 删除片段的空构造函数