7

我是 Android/Java 的新手(通常使用 PHP 和 JavaScript)。我已经阅读了几篇关于当引用被错误地使用时应用程序的内存泄漏问题的文章,所以我有一个关于我在其他人的工作中经常看到的问题的问题。

很多人,当他们需要在多个方法中访问诸如 Views 之类的东西时,会在创建 Activity 期间分配的属性中保留对 this 的引用。从我读过的内容(或至少理解我读过的内容)来看,这是内存泄漏的课程之一?

为对象分配一个 ID,然后在每个方法中搜索它们会更好吗?如果是这样,那么动态创建的对象呢?

4

4 回答 4

2

如果你正确使用它们很好,如果你不使用它们就很糟糕。

仅当您将活动之外的某些内容传递给另一个类并且该类的生命周期大于活动的生命周期时,您才会泄漏。一旦它不再是前台活动,Android 可能会破坏您的活动,并且如果活动之外的东西持有对它的引用,那么垃圾收集器将无法将内存释放回堆。

对活动上下文、静态和单例要特别小心。

简单地在活动中保留对视图的引用是绝对正常的。

这是一个坏的例子(伪代码);

public class MyApplication extends Application{
    public static ImageView activityBackgroundImageView;
}

public class MyActivity extends Activity{

    ImageView iv = findViewById(R.id.myImageView);
    myApplication.activityBackgroundImageView= iv; // <==== LEAK

}

实际上,泄漏并不存在,它仅在(如果) myActivity 完成()或销毁时泄漏。

每个对象都有一个引用计数——有多少对象持有对它的引用。在 Activity 中设置对 ImageView 的引用后,引用计数为 1。然后将该引用复制到 Application 类。注意。Java 中的所有内容都是按值传递的,因此您传递引用的值,正是该值的副本——即对同一对象的新引用。ImageView 上的引用计数现在是两个。

稍后,您完成()您的活动并且引用计数减少。现在是一个。垃圾收集器无法释放该 ImageView 对象,因为它的引用计数不为零。

当然,您可以通过将应用程序中的引用设为空来修复它,但您现在有了意大利面条代码。

于 2012-12-04T13:02:14.000 回答
0

感谢您的回答,只是想在创建一个会给人们带来问题的应用程序之前先确定一下。

作为一项安全措施,我添加了 onStop 和 onRestart 来删除和重新创建引用。这应该确保应用程序在后台时不存在任何此类内容,尽管我从未在静态属性中使用引用。

于 2012-12-10T17:56:48.690 回答
0

Context您在 Android 中提到的泄漏问题发生在对也持有对对象的引用的事物的引用被绑定到 UI 的事物持有时。在其包含的活动中保持对视图的引用,因为在某些时候该活动将不再被系统中的任何东西引用(之后onDestroy()),并且因为不再有来自垃圾收集根的引用(如全局变量) ,它有资格进行垃圾收集。换句话说,一旦没有其他人持有对该活动的引用,在活动及其视图之间建立的循环引用就不再重要。

问题出现的地方是发送对活动的引用——它本身就是一个Context——在 UI 和活​​动生命周期调用之外。诸如位置侦听器之类的东西或您忘记注销的东西。这将保持该引用使整个树没有资格进行垃圾收集。因此,大泄漏。

于 2012-12-04T13:00:26.603 回答
0

您对内存泄漏原因的观察并不完全正确。

存储对视图元素的引用非常好,您如何执行此操作并使用它们可能会导致内存泄漏。例如,避免使用静态引用,例如,如果您静态引用位图图像,您可能会无意中导致垃圾收集问题,正如 Simon 在他的回答中指出的那样。

因此,可以执行以下操作。

class{
 private TextView myTextView;

onCreate()
 myTextView = findViewById(R.id.mytextview);

myMethod()
 myTextView.text = "hello view."

}

myMethod 使用现有的参考纯粹是为了方便,没有什么能阻止你把

findViewById(R.id.mytextview).text = "hello view";

但是,如果您有很多参考资料,那将导致代码非常不可读。因此,您可以使用局部范围变量。

myMethod()
 TextView myTextView = findViewById(R.id.mytextview);
 myTextView.text = "Hello"
 .....

根据您的个人喜好,它不一定会导致内存泄漏。

现在这里的问题是 findViewById 是一个密集的过程,所以你真的不想重复调用它。列表视图特别容易出现这种情况,如果你不满足这种情况,它会显着减慢速度。

因此,在列表视图中,您会发现人们实现了 viewHolder 模式。您分配对视图子元素的引用的小对象。然后将此对象分配给父视图的 Tag 属性。在对视图的后续调用期间,您测试查看视图标记属性是否具有 viewHolder,如果有,它将引用子对象,从而节省您每次需要更新视图内容时调用 findViewById 的时间和精力。

非常粗略的想法,其实现方式略有不同。viewHolder = 新的 ViewHolder(); viewHolder.myTextField = findViewById(R.id.mytextview); myView.setTag(viewHolder) .... if (viewHolder) viewHolder.text = "hello"

请注意,您只能在列表视图中使用它。我没有将其用作一般的经验法则。

查找列表视图适配器的有效视图持有者模式。

于 2012-12-04T13:40:30.103 回答