作为静态内部类和独立公共类,Fragment的设计逻辑是什么?

28
我无法理解 Android 软件设计中的一个重要方面,这是我刚开始接触的。据我所知,Fragment 设计已经被采用来解耦代码,其中的直觉是 Activity 保持不变,Fragment 可以在其他地方重复使用,甚至可以在不同的 Activity 中使用,或者与其他 Fragment 一起在像主/细节流或横向 UI 这样的东西中使用。
好吧,我看到了不少关于为什么将 Fragment 放置为静态内部类在 Activity 中的问题,答案是如果我们不将它们设置为静态的,那么 Fragment 可能会保存对 Activity 的引用,屏幕旋转或重新绘制等操作可能会泄漏 Activity 或其他内容。
这让我回到了原点,我的问题是,既然 Fragment 设计已经被采用来解耦代码,那么为什么要将 Fragment 嵌入到 Activity 类中并将它们放在 Activity 的内部,而不是将它们作为独立的公共类呢?这难道不完全背道而驰于 Fragment 的存在吗?
每个 Fragment 作为自己的单独类看起来更加整洁、解耦和可重用,尤其是当尝试做动画时,Fragment 代码可能会增长到 1000 行左右。
  • 我们是否仍然会受到上述内存泄漏问题的困扰?
  • 我是否对设计逻辑有所遗漏?请指教。
  • 是否有一种方法可以使 Fragment 保持为内部类并在其他地方使用?或许我也有所遗漏,请告诉我。
项目结构任何其他的项目设计方法、概念或纠正我的直觉都非常欢迎,因为我刚开始在这里工作,我很想知道所有的选项。谢谢 :)

1
请问您能否添加一个链接,以支持将片段作为静态内部类添加到活动中。这听起来像是一件荒谬的事情。我总是将片段从活动中解耦,并将它们放在自己的类和.java文件中,没有任何活动泄漏或其他这样的废话。 - britzl
@britzl,比如说在Android Studio中创建新的活动时,默认情况下Fragment(PlaceholderFragment)会作为静态内部类嵌入到活动中,我必须手动将其从活动中取出并放入自己的类中。这是默认行为...这引发了我的疑问。 - Abdullah Khan
还有在Fragments的官方文档中,http://developer.android.com/guide/components/fragments.html#Design "public static class ExampleFragment extends Fragment" 在“添加用户界面”下。 - Abdullah Khan
啊,我之前没有注意到官方文档中的“public static class”表示法。不管怎样,在我的看法中,Fragment 应该被视为可重用的 UI 片段,无论是为了横向和纵向模式还是在多个不同的 Activity 中使用。当然,有时候一个 Fragment 只在单个 Activity 中使用,但在这种情况下,我会质疑为什么它首先要被制作成 Fragment。 - britzl
@Akay 你说得对,谷歌文档中许多示例都使用“公共静态类内部活动”,而片段则使用“片段事务”这样的术语,在片段管理器中非常荒谬!我记得数据库中有“提交”和“回滚”表达式。实际上,我不喜欢Android架构,而且谷歌的架构团队可能需要学习设计和模式,因为对我来说,Android就是:为了更少的结果而编写更多的代码行! - Mateus
2个回答

24
这使我回到原点,并且我有问题,如果Fragment设计被采用以分离代码,那么为什么我们要将其放在Activity类内部而不是作为独立的公共类呢?这难道不完全与Fragment的存在背道而驰吗? 主要驱动因素是组合而非解耦:可以轻松地在不同配置中组合在一起的可重用用户界面片段。从组合性导出模块化和解耦。所以确实有解耦,但并非主要关注点。
阅读有关fragment设计哲学的信息
模块化的另一面是:如果两个事物彼此属于,例如一个活动和一个仅与该活动一起使用的片段,则最好将它们保持在一起,而不是散布在整个代码库中,以便更容易地一起发展。在你提问的项目结构中,活动和碎片位于不同的包中,这样做并没有遵循这个原则。
通常看到的一种方法是将片段保留在单独的类中,但靠近相关的活动等,以便使用名称前缀,因此它们可以在同一包中的字母表列表中排序:例如,FooActivity与FooDetailsFragment。
对于简单的片段,其中没有太多的代码,并且该片段仅在一个活动中使用,将它们作为该活动中的静态内部类是完全可以的。
只需尝试保持一致,以便其他人可以轻松地在代码库中找到自己的路线,并且WTFs/分钟代码指标保持较低。
我们还会遇到上面提到的内存泄漏问题吗?
不会,泄漏仅适用于持有对外部类的引用的非静态内部类。包级别类没有任何外部类来持有引用。
由于片段共享托管活动的生命周期,因此泄漏外部对象并不是片段的关注点。只是框架需要能够在没有任何外部类对象的情况下实例化碎片,而静态是必需的。

有没有一种方法可以让Fragment保持为内部类并在其他地方使用?如果我漏掉了某些东西,请告诉我。

您可以在代码中使用点符号Outer.Inner或在反射中使用Outer$Inner(例如XML文件)来引用公共内部类。

虽然这在技术上是可能的,但这样做意味着您依赖于类的内部机制,这是设计上的问题。 我会在外部类代码本身中引用内部类。


2
感谢您抽出时间回答我的问题,我真的很感激您将我的问题的每个部分都解释得如此清晰明了。我提出这个问题的一个显而易见的原因是因为Android Studio默认创建了所有我的片段和静态内部类...我不想在不知道自己在做什么的情况下超越默认设置。从您的解释中,我感觉Android Studio假设Fragment仅用于特定的Activity。 - Abdullah Khan
“我真的更喜欢在外部类代码本身中使用内部类。”,“我不太理解你回答中的这部分,你是什么意思?” - Eduardo Naveda
如果你有OuterClass.InnerFragment,请只在OuterClass内使用InnerFragment。片段应该是模块化的;将它们放在其他类中会降低模块性。 - laalto

3
我希望更好地了解您的想法背后的逻辑; 我经常在它们自己的类中创建片段,我的应用程序在重新定位期间运行得很稳定。 我确实使用接口与管理活动进行通信,使用适配器来管理片段,并执行基本会计工作以确保我不会从重新加载中启动多个后台线程。
因此,我查看http://developer.android.com/guide/components/fragments.html,这始终值得重新阅读,而且底部的示例正是您建议的内容,我每天都在使用:两个公共片段位于独立的类中,并由一个活动附加和连接。
如果有人真的建议您所说的内容,我怀疑他们太过于字面理解“片段必须始终嵌入活动中,片段的生命周期直接受到主机活动生命周期的影响。”;他们的意思是嵌入在运行时。
祝您好运!

就像我在评论中提到的那样,我的来源是Android Studio的默认行为和官方文档的部分内容。虽然我认为这很有用:“我使用接口与管理Activity通信,使用适配器来管理Fragment,并执行基本的计算以确保我不会从重新加载中启动多个后台线程。”但我还没有尝试过这样做。我会看看它是否能改善我的实践。 - Abdullah Khan
1
很酷,没错,不要害怕使用类;一般来说,能够这样做可以确保你正在操作的对象是良好形成的,并且正在执行你认为它应该执行的操作。考虑契约设计模式:在完美的世界中,每个对象只与构造函数、静态对象和它创建的对象一起工作。片段很好地实现了这种哲学,实现接口是保持在范围内的好方法,但这+类系统会阻止某些操作。 - anthropic android

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接