2012-12-04 16 views
7

我是Android/Java中的新手(通常使用PHP和JavaScript)。我已经阅读了一些关于应用程序内存泄漏问题的文章,这些文章以错误的方式使用引用,所以我有一个关于我在其他人的工作中经常见到的问题的问题。包含引用好/坏的属性

很多人,当他们需要访问像视图和在等多种方法的东西,保持在其中创建活动的过程中分配财产的这个参考。从我读过的内容(或者至少理解了我读过的内容),这是内存泄漏的课程之一?

是更好的ID分配给对象,然后在每个方法寻找他们呢?如果是这样,那么动态创建的对象呢?

回答

0

在Android中引用的泄漏问题出现在引用了也引用了Context对象的事物时,它们被而非绑定到UI。在其包含活动中持有对视图的引用,因为某些时候系统中的任何内容都不会再引用该活动(在onDestroy()之后),并且由于不再从垃圾收集根目录引用它(如全局变量),它有资格进行垃圾收集。换句话说,一旦活动与其视图之间建立的循环引用不再重要,否则其他持有对该活动的引用。

问题出在哪里发送活动的引用 - 本身 a Context - UI和活动生命周期调用之外。像位置侦听器或某些您忘记注销的内容。这将持有该参考使整个树不适合垃圾收集。因此,大泄漏。

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完成()或销毁时泄漏。

每个对象都有一个引用计数 - 有多少对象保持对它的引用。在您的活动中设置对ImageView的引用后,引用计数为1。然后,您将该引用复制到Application类。 NB。 Java中的所有东西都是按值传递的,因此您可以传递引用的值,即值的副本 - 即对同一对象的新引用。 ImageView上的引用计数现在是两个。

一段时间后,你完成()的活动,并引用计数递减。现在是一个。垃圾收集器不能释放该ImageView对象,因为它具有非零引用计数。

当然,您可以通过将应用程序中的引用置零来修复它,但您现在已经有意大利细面条代码。

+1

对图像的静态引用特别是一个问题,因为它们自身显然以某种方式绑定到活动。所以对位图对象的静态引用将意味着该活动永远不会被释放。 – Emile

+0

是的,和任何人谁是困惑,我原来的答复使用一个静态的位图这是我后来简化为参照,将泄漏的整个活动,因为它有到它实例化活动的背景下,参考了ImageView的。 – Simon

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模式。一个小对象,您可以将视图子元素的引用分配给。然后将该对象分配给父视图标签属性。在后续对视图的调用期间,您将测试视图标签属性是否具有viewHolder,如果它包含,则会引用子对象,这样每次需要更新视图内容时都可以节省调用findViewById的时间和精力。

非常粗略的想法,其实施方式略有不同。 viewHolder = new ViewHolder(); viewHolder.myTextField = findViewById(R.id.mytextview); myView.setTag(viewHolder) .... 如果(viewHolder) viewHolder.text = “你好”

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

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

0

感谢您的答案,只是想确保在创建一个应用程序,会产生问题的人。

作为一项安全措施,我添加了onStop和onRestart以删除和重新创建参考。这应该确保在应用程序处于后台时不存在这种类型,尽管我从不在静态属性中使用引用。