gpt4 book ai didi

java - 什么时候使用(匿名)内部类是安全泄漏的?

转载 作者:行者123 更新时间:2023-12-01 16:24:02 27 4
gpt4 key购买 nike

我一直在阅读一些关于 Android 内存泄漏的文章,并观看了来自 Google I/O 的这个有趣的视频 on the subject .

尽管如此,我仍然不完全理解这个概念,尤其是当它对用户安全或危险时 Activity 中的内部类 .

这是我的理解:

如果内部类的实例比其外部类( Activity )存活时间更长,则会发生内存泄漏。
-> 在哪些情况下会发生这种情况?

在这个例子中,我认为没有泄漏的风险,因为匿名类无法扩展 OnClickListener会比 Activity 活得久吧?

    final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_generic);
Button okButton = (Button) dialog.findViewById(R.id.dialog_button_ok);
TextView titleTv = (TextView) dialog.findViewById(R.id.dialog_generic_title);

// *** Handle button click
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
dialog.dismiss();
}
});

titleTv.setText("dialog title");
dialog.show();

现在,这个例子是否危险,为什么?
// We are still inside an Activity
_handlerToDelayDroidMove = new Handler();
_handlerToDelayDroidMove.postDelayed(_droidPlayRunnable, 10000);

private Runnable _droidPlayRunnable = new Runnable() {
public void run() {
_someFieldOfTheActivity.performLongCalculation();
}
};

我对理解这个主题与详细理解当一个 Activity 被销毁和重新创建时保留的内容有关这一事实有疑问。

是吗?

假设我只是改变了设备的方向(这是最常见的泄漏原因)。当 super.onCreate(savedInstanceState)将在我的 onCreate() 中调用,这是否会恢复字段的值(就像它们在方向改变之前一样)?这也会恢复内部类的状态吗?

我意识到我的问题不是很精确,但我真的很感激任何可以使事情更清楚的解释。

最佳答案

你问的是一个非常棘手的问题。虽然您可能认为这只是一个问题,但实际上您同时在问几个问题。我会尽我所能,我必须涵盖它的知识,希望其他一些人能加入我可能会遗漏的内容。

嵌套类:简介

由于我不确定您对 Java 中的 OOP 是否满意,因此这将涉及一些基础知识。嵌套类是指类定义包含在另一个类中。基本上有两种类型:静态嵌套类和内部类。它们之间的真正区别是:

  • 静态嵌套类:
  • 被认为是“顶级”。
  • 不需要构造包含类的实例。
  • 不能在没有明确引用的情况下引用包含的类成员。
  • 有自己的一生。
  • 内部嵌套类:
  • 始终需要构造包含类的实例。
  • 自动具有对包含实例的隐式引用。
  • 可以在没有引用的情况下访问容器的类成员。
  • 生命周期应该不超过容器的生命周期。

  • 垃圾收集和内部类

    垃圾收集是自动的,但会根据是否认为对象正在被使用来尝试删除对象。垃圾收集器非常聪明,但并非完美无缺。它只能通过是否存在对对象的 Activity 引用来确定是否正在使用某物。

    这里真正的问题是内部类的存活时间比它的容器长。这是因为对包含类的隐式引用。发生这种情况的唯一方法是,如果包含类之外的对象保留对内部对象的引用,而不考虑包含对象。

    这可能导致内部对象还活着(通过引用)但对包含对象的引用已经从所有其他对象中删除的情况。因此,内部对象使包含对象保持 Activity 状态,因为它始终具有对它的引用。这样做的问题是,除非对其进行编程,否则无法返回包含对象以检查它是否还活着。

    这种实现最重要的方面是,无论它是在 Activity 中还是在 drawable 中都没有区别。你会 总是 使用内部类时必须有条不紊,并确保它们永远不会超过容器的对象。幸运的是,如果它不是代码的核心对象,相比之下,泄漏可能很小。不幸的是,这些是最难找到的一些泄漏,因为在其中许多泄漏之前,它们可能会被忽视。

    解决方案:内部类
  • 从包含对象中获取临时引用。
  • 允许包含对象成为唯一一个保持对内部对象的长期引用的对象。
  • 使用已建立的模式,例如工厂。
  • 如果内部类不需要访问包含的类成员,请考虑将其转换为静态类。
  • 无论是否在 Activity 中,请谨慎使用。

  • Activity 与观点:简介

    Activity 包含大量信息以便能够运行和显示。 Activity 由它们必须具有 View 的特性定义。他们也有某些自动处理程序。无论您是否指定,Activity 都隐式引用了它所包含的 View。

    为了创建一个 View ,它必须知道在哪里创建它以及它是否有任何子项以便它可以显示。这意味着每个 View 都有对 Activity 的引用(通过 getContext() )。此外,每个 View 都保留对其 subview 的引用(即 getChildAt() )。最后,每个 View 都保留对表示其显示的渲染 Bitmap 的引用。

    每当您有对 Activity (或 Activity 上下文)的引用时,这意味着您可以沿着整个链向下布局层次结构。这就是为什么有关 Activity 或 View 的内存泄漏如此严重的原因。它可以是 一次全部泄漏的内存。

    Activity 、 View 和内部类

    鉴于以上有关内部类的信息,这些是最常见的内存泄漏,但也是最常避免的。虽然希望内部类可以直接访问 Activity 类成员,但许多人愿意将它们设为静态以避免潜在问题。 Activity 和 View 的问题远不止于此。

    泄露的 Activity 、 View 和 Activity 上下文

    这一切都归结为上下文和生命周期。有某些事件(例如方向)会杀死 Activity 上下文。由于如此多的类和方法需要 Context,开发人员有时会尝试通过获取对 Context 的引用并保留它来保存一些代码。碰巧的是,我们必须创建以运行 Activity 的许多对象必须存在于 Activity LifeCycle 之外,以便允许 Activity 执行它需要执行的操作。如果您的任何对象在销毁时碰巧拥有对 Activity、其上下文或其任何 View 的引用,则您刚刚泄漏了该 Activity 及其整个 View 树。

    解决方案: Activity 和 View
  • 不惜一切代价避免对 View 或 Activity 进行静态引用。
  • 所有对 Activity 上下文的引用都应该是短暂的(函数的持续时间)
  • 如果您需要长期存在的上下文,请使用应用程序上下文( getBaseContext()getApplicationContext() )。这些不会隐式保留引用。
  • 或者,您可以通过覆盖配置更改来限制对 Activity 的破坏。但是,这并不能阻止其他潜在事件破坏 Activity。虽然您可以这样做,但您可能仍想引用上述做法。

  • Runnables:简介

    Runnables 实际上并没有那么糟糕。我的意思是,它们可能是,但实际上我们已经到达了大部分危险区域。 Runnable 是一种异步操作,它执行独立于创建它的线程的任务。大多数可运行对象是从 UI 线程实例化的。本质上,使用 Runnable 是创建另一个线程,只是稍微多一点管理。如果您将 Runnable 分类为标准类并遵循上述指南,您应该会遇到一些问题。现实情况是,许多开发人员并没有这样做。

    出于易用性、可读性和逻辑程序流程的考虑,许多开发人员使用匿名内部类来定义他们的 Runnable,例如您在上面创建的示例。这会产生一个类似于您在上面键入的示例。匿名内部类基本上是一个离散的内部类。您只是不必创建一个全新的定义,只需覆盖适当的方法即可。在所有其他方面,它是一个内部类,这意味着它保持对其容器的隐式引用。

    Runnables 和 Activity/ View

    好极了!这部分可以很短!由于 Runnables 在当前线程之外运行,因此它们的危险在于长时间运行的异步操作。如果在 Activity 或 View 中将 runnable 定义为匿名内部类或嵌套内部类,则存在一些非常严重的危险。这是因为,如前所述,它 知道它的容器是谁。输入方向更改(或系统终止)。现在只需回顾前面的部分即可了解刚刚发生的事情。是的,你的例子很危险。

    解决方案:Runnables
  • 如果它不会破坏您的代码逻辑,请尝试扩展 Runnable。
  • 如果扩展的 Runnable 必须是嵌套类,请尽量使它们成为静态的。
  • 如果您必须使用匿名 Runnables,请避免在 中创建它们。任何 对正在使用的 Activity 或 View 具有长期引用的对象。
  • 许多 Runnable 可以很容易地成为 AsyncTask。考虑使用 AsyncTask,因为默认情况下它们是 VM 管理的。

  • 回答最后的问题
    现在回答本文其他部分没有直接解决的问题。你问“什么时候内部类的对象可以比外部类存活得更久?”在我们开始之前,让我再次强调:尽管您在 Activity 中担心这一点是正确的,但它可能会导致任何地方的泄漏。我将提供一个简单的例子(不使用 Activity )只是为了演示。

    下面是一个基本工厂的常见示例(缺少代码)。
    public class LeakFactory
    {//Just so that we have some data to leak
    int myID = 0;
    // Necessary because our Leak class is an Inner class
    public Leak createLeak()
    {
    return new Leak();
    }

    // Mass Manufactured Leak class
    public class Leak
    {//Again for a little data.
    int size = 1;
    }
    }

    这不是一个常见的例子,但足够简单来演示。这里的关键是构造函数......
    public class SwissCheese
    {//Can't have swiss cheese without some holes
    public Leak[] myHoles;

    public SwissCheese()
    {//Gotta have a Factory to make my holes
    LeakFactory _holeDriller = new LeakFactory()
    // Now, let's get the holes and store them.
    myHoles = new Leak[1000];

    for (int i = 0; i++; i<1000)
    {//Store them in the class member
    myHoles[i] = _holeDriller.createLeak();
    }

    // Yay! We're done!

    // Buh-bye LeakFactory. I don't need you anymore...
    }
    }

    现在,我们有泄漏,但没有工厂。即使我们释放了工厂,它也会保留在内存中,因为每个泄漏都有一个对它的引用。外部类没有数据也无所谓。这种情况比人们想象的要频繁得多。我们不需要创造者,只需要它的创造物。所以我们暂时创造一个,但无限期地使用这些创造物。

    想象一下,当我们稍微改变构造函数时会发生什么。
    public class SwissCheese
    {//Can't have swiss cheese without some holes
    public Leak[] myHoles;

    public SwissCheese()
    {//Now, let's get the holes and store them.
    myHoles = new Leak[1000];

    for (int i = 0; i++; i<1000)
    {//WOW! I don't even have to create a Factory...
    // This is SOOOO much prettier....
    myHoles[i] = new LeakFactory().createLeak();
    }
    }
    }

    现在,这些新的 LeakFactories 中的每一个都被泄露了。你对那个怎么想的?这是两个非常常见的例子,说明内部类如何比任何类型的外部类更长寿。如果那个外部类是一个 Activity,想象一下它会变得多么糟糕。

    结论

    这些列出了不恰当使用这些对象的主要已知危险。总的来说,这篇文章应该已经涵盖了你的大部分问题,但我知道这是一个 loooong 帖子,所以如果你需要澄清,请告诉我。只要按照上面的做法去做,就不用担心漏电了。

    关于java - 什么时候使用(匿名)内部类是安全泄漏的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10864853/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com