当完全被其泄露安全使用(匿名)内部类?部类、安全

2023-09-12 00:58:00 作者:一个缺爱嘀孩纸

我一直在阅读关于Android的内存泄漏的一些文章,看着从谷歌这个有趣的视频I / O 关于这个问题的。

I have been reading some articles on memory leaks in Android and watched this interesting video from Google I/O on the subject.

不过,我不完全理解的概念,特别是当它是安全的或危险的用户的内部类的活动里面

Still, I don't fully understand the concept, and especially when it is safe or dangerous to user inner classes inside an Activity.

这是我的理解:

如果一个内部类的实例,生存比其外部类(的活动)不再会发生内存泄漏。 - > 在哪些情况下能这样呢

A memory leak will occur if an instance of an inner class survives longer than its outer class (an Activity). -> In which situations can this happen?

在这个例子中,我假设有泄漏的危险,因为没有办法匿名类扩展 OnClickListener 将活得比活动,对吧?

In this example, I suppose there is no risk of leak, because there is no way the anonymous class extending OnClickListener will live longer than the activity, right?

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

现在,是危险的这个例子,为什么?

Now, is this example dangerous, and why?

// We are still inside an Activity
_handlerToDelayDroidMove = new Handler();
_handlerToDelayDroidMove.postDelayed(_droidPlayRunnable, 10000);

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

我有关于该理解这个话题,是因为有了解详细当一个活动被破坏并重新创建的内容保持这一事实毋庸置疑。

I have a doubt regarding the fact that understanding this topic has to do with understanding in detail what is kept when an activity is destroyed and re-created.

是吗?

让说,我只是改变了设备(这是泄漏的最常见的原因)的方向。当 super.onCreate(savedInstanceState)将被称为我的的onCreate(),将在恢复字段的值(因为他们的方向变化前)?这会不会又恢复内部类的状态?

Let say I just changed the orientation of the device (which is the most common cause of leaks). When super.onCreate(savedInstanceState) will be called in my onCreate(), will this restore the values of the fields (as they were before orientation change)? Will this also restore the states of inner classes?

我意识到我的问题是不是很precise,但我真的AP preciate任何解释,可能使事情更加清楚。

I realize my question is not very precise, but I'd really appreciate any explanation that could make things clearer.

推荐答案

塞巴斯蒂安,

你所问的是一个pretty的很难回答的问题。虽然你可能觉得这只是一个问题,你实际上是在问几个问题一次。我会尽我所能与我要掩盖它,并希望知识,一些人将加入到支付什么,我可能会错过。

What you are asking is a pretty tough question. While you may think it is just one question, you are actually asking several questions at once. I'll do my best with the knowledge that I have to cover it and, hopefully, some others will join in to cover what I may miss.

内部类:简介

由于我不知道你怎么舒服与OOP在Java中,这会打击几个基础知识。一个内部类是当一个类定义包含另一个类中。基本上有两种类型:静态和非静态的。它们之间真正的区别是:

As I'm not sure how comfortable you are with OOP in Java, this will hit a couple of basics. An inner class is when a class definition is contained within another class. There are basically two types: static and non-static. The real difference between these are:

在静态内部类: 在被视为顶级。 请不要要求包含类的实例构造。 不可以引用包含类的成员没有一个明确的参考。 拥有自己的一生。 Static inner classes: Are considered "top-level". Do not require an instance of the containing class to be constructed. May not reference the containing class members without an explicit reference. Have their own lifetime. 始终需要包含类的实例构造。 自动有一个隐含的引用含有实例。 可以访问容器的类成员,而不引用。 寿命是的应该的是不超过该容器的。 Always require an instance of the containing class to be constructed. Automatically have an implicit reference to the containing instance. May access the container's class members without the reference. Lifetime is supposed to be no longer than that of the container.

垃圾回收和非静态内部类

垃圾收集是自动的,但试图移除根据是否认为他们正在使用的对象。垃圾收集器pretty的聪明,但并非无懈可击。可如果事情是正在使用的是否存在是一个积极的参考对象只能决定。

Garbage Collection is automatic but tries to remove objects based on whether it thinks they are being used. The Garbage Collector is pretty smart, but not flawless. It can only determine if something is being used by whether or not there is an active reference to the object.

在这里真正的问题是,当一个非静态内部类一直维持生命超过其容器。这是因为,隐式引用包含类的。可能发生这种情况的唯一方法是,如果包含类之外的对象有一个参考的内部对象,不考虑包含的对象。

The real issue here is when a Non-Static Inner Class has been kept alive longer than its container. This is because of the implicit reference to the containing class. The only way this can occur is if an object outside of the containing class keeps a reference to the inner object, without regard to the containing object.

这可能导致一种情况的内对象是活(通过参考),但引用包含对象已经从所有其他对象中删除。内部对象,因此,保持包含对象还活着,因为它的总是的有一个参考吧。这样做的问题是,除非它已被编程,也没有办法让回包含对象,以检查它是否是偶数活着。

This can lead to a situation where the inner object is alive (via reference) but the references to the containing object has already been removed from all other objects. The inner object is, therefore, keeping the containing object alive because it will always have a reference to it. The problem with this is that unless it is programmed, there is no way to get back to the containing object to check if it is even alive.

要认识到这一点,最重要的方面是,它使没有区别是否是一个活动,或者是绘制。你会的的总是的在使用非静态内部类,并确保他们从来没有活得比容器的对象时要有条理。幸运的是,如果不是你的code核心对象时,泄漏可能是小的比较。不幸的是,这些都是一些最难泄漏找到的,因为它们很可能被忽视,直到其中许多已经泄漏。

The most important aspect to this realization is that it makes no difference whether it is in an Activity or is a drawable. You will always have to be methodical when using non-static inner classes and make sure that they never outlive objects of the container. Luckily, if it isn't a core object of your code, the leaks may be small in comparison. Unfortunately, these are some of the hardest leaks to find, because they are likely to go unnoticed until many of them have leaked.

解决方案:非静态内部类

从包含对象增益临时引用。 允许包含对象是唯一一个保持长寿命的引用到内的对象。 建立使用模式,比如工厂。 如果内部类不需要访问包含类的成员,可以考虑把它变成一个静态类。 使用谨慎,无论是在活动或没有。

活动和观点:简介

活动包含了大量的信息,以便能够运行和显示。活动由它们必须有一个观点的特征定义。他们也有一定的自动处理程序。无论您指定与否,活动有一个隐含的引用它包含的视图。

Activities contain a lot of information to be able to run and display. Activities are defined by the characteristic that they must have a View. They also have certain automatic handlers. Whether you specify it or not, the Activity has an implicit reference to the View it contains.

为了要创建一个视图,它必须知道在哪里创建,以及是否有任何的孩子,以便它可以显示。这意味着,每一个视图都有一个参考活动(通过的getContext())。此外,每一个视图来引用其子(即 getChildAt())。最后,每个视图有一个参考,呈现的位图重新presents它的显示。

In order for a View to be created, it must know where to create it and whether it has any children so that it can display. This means that every View has an reference to the Activity (via getContext()). Moreover, every View keeps references to its children (i.e. getChildAt()). Finally, each View keeps a reference to the rendered Bitmap that represents its display.

只要你有一个参考的活动(或活动上下文),这意味着你可以按照整个链条下来的布局层次。这就是为什么有关的活动或查看内存泄漏是如此巨大的交易。它可以是内存中的吨一次被泄露所有。

Whenever you have a reference to an Activity (or Activity Context), this means that you can follow the ENTIRE chain down the layout hierarchy. This is why memory leaks regarding Activities or Views are such a huge deal. It can be a ton of memory being leaked all at once.

活动,视图和非静态内部类

鉴于上述关于内部类的信息,这些是最常见的内存泄漏,但也是最常用的避免。虽然最好是有一个内部类直接访问的活动类的成员,很多都愿意只是让他们静以避免潜在的问题。与活动和观点的问题比这深得多。

Given the information above about Inner Classes, these are the most common memory leaks, but also the most commonly avoided. While it is desirable to have an inner class have direct access to an Activities class members, many are willing to just make them static to avoid potential issues. The problem with Activities and Views goes much deeper than that.

泄露活动,视图和活动上下文

这一切都归结到上下文和生命周期。有某些事件(如方向),将杀死一个活动上下文。既然有这么多的类和方法需要一个背景下,开发商有时会试图通过抓住一个参照上下文并保持到它节省一些code。它只是恰巧,许多我们要创建来运行我们的活动对象必须在活动生命周期之外存在是为了让活动做需要做的事情。如果您的对象碰巧有一个参考的活动,它的语境,或任何其时,它被摧毁意见,你刚刚泄露的活动和整个景观树。

It all comes down to the Context and the LifeCycle. There are certain events (such as orientation) that will kill an Activity Context. Since so many classes and methods require a Context, developers will sometimes try to save some code by grabbing a reference to a Context and holding onto it. It just so happens that many of the objects we have to create to run our Activity have to exist outside of the Activity LifeCycle in order to allow the Activity to do what it needs to do. If any of your objects happen to have a reference to an Activity, its Context, or any of its Views when it is destroyed, you have just leaked that Activity and its entire View tree.

解决方案:活动和观点

避免,不惜一切代价,使静态引用视图或活动。 要活动上下文的所有引用应该是短暂的(功能的持续时间) 如果您需要一个长期存在背景下,使用应用程序上下文( getBaseContext() getApplicationContext() )。这些不守隐式引用。 或者,您可以通过覆盖配置更改限制了活动的破坏。但是,这并不破坏活动停止其他潜在事件。当你的可以的做到这一点,你可能还是要参照上述做法。 Avoid, at all costs, making a Static reference to a View or Activity. All references to Activity Contexts should be short lived (the duration of the function) If you need a long-lived Context, use the Application Context (getBaseContext() or getApplicationContext()). These do not keep references implicitly. Alternatively, you may limit the destruction of an Activity by overriding Configuration Changes. However, this does not stop other potential events from destroying the Activity. While you can do this, you may still want to refer to the above practices.

的Runnable:简介

的Runnable实际上没有那么糟糕。我的意思是,他们的可以的是,但实际上我们已经打到最危险的区域。一个可运行的是执行从它创建的线程任务独立的异步操作。大多数可运行是从UI线程实例化。从本质上说,使用一个Runnable正在创造另一个线程,只是稍微进行管理。如果你的类一个Runnable像一个标准的类,并按照上述准则,你应该遇到一些问题。现实情况是,许多开发商不这样做。

Runnables are actually not that bad. I mean, they could be, but really we've already hit most of the danger zones. A Runnable is an asynchronous operation that performs a task independant from the thread it was created on. Most runnables are instantiated from the UI thread. In essence, using a Runnable is creating another thread, just slightly more managed. If you class a Runnable like a standard class and follow the guidelines above, you should run into few problem. The reality is that many developers do not do this.

出轻松,可读性和逻辑程序流程,许多开发商利用匿名内部类来定义自己的Runnable,如您在上面创建的例子。这导致像你上面键入一个例子。匿名内部类基本上是一个独立的非静态内部类。你只是没有创建一个全新的定义,简单地重写适当的方法。在所有其他方面,它是一个非静态内部类,这意味着它保持一个隐含的引用它的容器。

Out of ease, readability and logical program flow, many developers utilize Anonymous Inner Classes to define their Runnables, such as the example you create above. This results in an example like the one you typed above. An Anonymous Inner Class is basically a discrete Non-Static Inner Class. You just don't have to create a whole new definition and simply override the appropriate methods. In all other respects it is a Non-Static Inner Class, which means that it keeps an implicit reference to its container.

的Runnable和活动/浏览次数

耶!这部分可以很短!由于是可运行当前线程之外运行的事实,这些危险涉及到长时间运行的异步操作。如果可运行在一个活动或查看一个匿名内部类和非静态内部类中定义,也有一些非常严重的危险。这是因为,previously说,它的的的的以知道是谁的容器。输入方向变化(或系统杀死)。现在只是重提了previous部分,以了解到底发生了什么。是的,你的榜样是相当危险的。

Yay! This section can be short! Due to the fact that Runnables run outside of the current thread, the danger with these comes to long running asynchronous operations. If the runnable is defined in an Activity or View as an Anonymous Inner Class OR Non-Static Inner Class, there are some very serious dangers. This is because, as previously stated, it has to know who its container is. Enter the orientation change (or system kill). Now just refer back to the previous sections to understand what just happened. Yes, your example is quite dangerous.

解决方案:的Runnable

尝试和扩展了Runnable,如果它不会破坏你的code中的逻辑。 请你最好使扩展的Runnable静态的,如果他们必须是内部类。 如果您必须使用匿名的Runnable,避免的任意的对象,它具有长寿命引用的活动或查看正在使用中。 在许多的Runnable可以很容易地被AsyncTasks。考虑使用的AsyncTask为那些虚拟机管理在默认情况下。

回答最后一个问题 现在回答这个问题不是的直接的问题通过这篇文章的其他部分解决。你问一个内部类的一个对象时,可以存活超过其外部类?在我们到达这一点,让我再次强调:虽然你是正确的担心这个活动,就可以在任何地方引起泄漏。我会提供一个简单的例子(不使用活动),只是为了演示。

Answering the Final Question Now to answer the questions that were not directly addressed by the other sections of this post. You asked "When can an object of an inner class survive longer than its outer class?" Before we get to this, let me reemphasize: though you are right to worry about this in Activities, it can cause a leak anywhere. I shall provide a simple example (without using an Activity) just to demonstrate.

下面是一个基本的工厂(缺少code)一个常见的​​例子。

Below is a common example of a basic factory (missing the code).

public class LeakFactory
{//Just so that we have some data to leak
    int myID = 0;
// Necessary because our Leak class is non-static
    public Leak createLeak()
    {
        return new Leak();
    }

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

这是一个并不常见的例子,但非常简单演示。这里的关键是构造...

This is a not as common example, but simple enough to demonstrate. The key here is the constructor...

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...
    }
}

现在,我们已经泄漏,但没有工厂。尽管我们公布的工厂,它会留在内存中,因为每一个泄漏有一个参考吧。它甚至没有重要的外部类没有任何数据。出现这种情况的次数远远多于人们想象。我们不需要创造者,只是它的创作。因此,我们暂时创建一个,但使用的创作下去。

Now, we have Leaks, but no Factory. Even though we released the Factory, it will remain in memory because every single Leak has a reference to it. It doesn't even matter that the outer class has no data. This happens far more often than one might think. We don't need the creator, just its creations. So we create one temporarily, but use the creations indefinitely.

想象一下,当我们改变构造稍稍会发生什么。

Imagine what happens when we change the constructor just slightly.

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每一个人刚刚被泄露。你怎么想的呢?这些是如何内部类可以经受住外部类的任何类型的两个非常常见的例子。如果外部类已经是一个活动,想象差多少它会一直。

Now, every single one of those new LeakFactories has just been leaked. What do you think of that? Those are two very common examples of how a inner class can outlive an outer class of any type. If that outer class had been an Activity, imagine how much worse it would have been.

结论

这些列表使用这些对象不恰当的主要已知的危险。在一般情况下,这篇文章应该已经覆盖大部分的问题,但我明白这是一个loooong岗位,所以如果你需要澄清,只是让我知道。只要您按照上面的做法,你将有泄漏很少担心。

These list the primarily known dangers of using these objects inappropriately. In general, this post should have covered most of your questions, but I understand it was a loooong post, so if you need clarification, just let me know. As long as you follow the above practices, you will have very little worry of leakage.

希望这有助于

FuzzicalLogic

FuzzicalLogic