我一直在读一些关于安卓系统内存泄漏的文章,并在谷歌I/O上观看了这段有趣的视频http://www.youtube.com/watch?v=_CruQY55HOk“>关于主题
尽管如此,我还是不完全理解这个概念,尤其是当用户在活动中的内部类安全或危险时
这就是我所理解的:
如果内部类的实例比其外部类(活动)存活时间长,则会发生内存泄漏。
->在哪些情况下会发生这种情况?
在本例中,我假设没有泄漏的风险,因为扩展OnClickListener的匿名类不可能比活动活得更长,对吗
最终对话框=新建对话框(此对话框);
setContentView(R.layout.dialog_generic);
按钮ok按钮=(按钮)对话框.findViewById(R.id.dialog\u按钮\u ok);
TextView titleTv=(TextView)dialog.findViewById(R.id.dialog\u generic\u title);
//***手柄按钮点击
setOnClickListener(新的OnClickListener(){
公共void onClick(视图v){
dialog.dismise();
}
});
titleTv.setText(“对话标题”);
dialog.show();
现在,这个例子危险吗?为什么
//我们仍在活动中
_HandlerToDelaHydroidMove=新处理程序();
_handlerToDelayDroidMove.postDelayed(_-droidPlayRunnable,10000);
private Runnable _droidPlayRunnable=new Runnable(){
公开募捐{
_SomeFieldofActivity.performLongCalculation();
}
};
我对理解这个主题与详细理解活动被销毁和重新创建时所保存的内容有关这一事实表示怀疑
是吗
比方说,我刚刚改变了设备的方向(这是最常见的泄漏原因)。当在我的onCreate()中调用super.onCreate(savedInstanceState)时,这是否会恢复字段的值(与方向更改之前一样)?这还会恢复内部类的状态吗
我知道我的问题不是很精确,但我真的很感激任何能让事情更清楚的解释
你问的是一个很难回答的问题。虽然你可能认为这只是一个问题,但实际上你同时问了几个问题。我会尽我所能去报道,希望其他人也能加入进来,报道我可能错过的内容
嵌套类:简介
由于我不确定您对Java中的OOP有多满意,这将触及一些基本问题。嵌套类是指一个类定义包含在另一个类中。基本上有两种类型:静态嵌套类和内部类。它们之间的真正区别是:
- 静态嵌套类:
- 被认为是“顶级的”
- 不需要构造包含类的实例
- 在没有显式引用的情况下,不能引用包含的类成员
- 有自己的一生
- 内部嵌套类:
- 始终需要构造包含类的实例
- 自动具有对包含实例的隐式引用
- 可以在没有引用的情况下访问容器的类成员
- 寿命应不超过容器的寿命
垃圾收集和内部类
垃圾收集是自动的,但会根据是否认为正在使用对象来尝试删除对象。垃圾收集器相当智能,但并非完美无缺。它只能通过是否存在对对象的活动引用来确定是否正在使用某个对象
这里真正的问题是,内部类的存活时间比其容器的存活时间长。这是因为对包含类的隐式引用。发生这种情况的唯一方法是,如果包含类之外的对象保留对内部对象的引用,而不考虑包含对象
这可能导致内部对象处于活动状态(通过引用),但对包含对象的引用已从所有其他对象中删除。因此,内部对象将保持包含对象的活动状态,因为它将始终具有对它的引用。问题在于,除非对其进行编程,否则无法返回包含对象以检查其是否处于活动状态
实现这一点最重要的一点是,无论它是在活动中还是在可绘制中,都没有区别。在使用内部类时,必须有条不紊地使用,并确保它们永远不会超过容器的对象。幸运的是,如果它不是代码的核心对象,那么相比之下,泄漏可能很小。不幸的是,这些是最难发现的泄密,因为在许多泄密之前,它们很可能会被忽视
解决方案:内部类
- 从包含对象获取临时引用
- 允许包含对象是唯一保留对内部对象的长期引用的对象
- 使用已建立的模式,例如工厂
- 如果内部类不需要访问包含的类成员,请考虑将其转换为静态类。
- 无论是否在活动中,请谨慎使用
活动和观点:介绍
活动包含大量信息,以便能够运行和显示。活动由其必须具有视图的特性定义。它们还具有某些自动处理程序。无论指定与否,该活动都具有对其包含的视图的隐式引用
为了创建视图,它必须知道在何处创建视图,以及是否有子视图,以便显示。这意味着每个视图都有对活动的引用(通过getContext())。此外,每个视图都保留对其子视图的引用(即getChildAt())。最后,每个视图保留对表示其显示的渲染位图的引用
每当您有一个对活动(或活动上下文)的引用时,这意味着您可以沿着布局层次结构的整个链。这就是为什么关于活动或视图的内存泄漏是如此巨大的问题。它可能是一次泄漏的大量内存
活动、视图和内部类
鉴于以上关于内部类的信息,这些是最常见的内存泄漏,也是最常避免的。虽然希望内部类能够直接访问活动类成员,但许多人愿意将其设置为静态以避免潜在问题。活动和观点的问题远不止于此
泄露的活动、视图和活动上下文
这一切都可以归结为上下文和生命周期。某些事件(如方向)将终止活动上下文。由于如此多的类和方法需要一个上下文,开发人员有时会试图通过获取对上下文的引用并保留它来保存一些代码。碰巧的是,我们为了运行活动而必须创建的许多对象必须存在于活动生命周期之外,以便让活动能够完成它需要做的事情。如果在某个活动、其上下文或其任何视图被销毁时,您的任何对象碰巧引用了该活动、其上下文或其任何视图,那么您刚刚泄漏了该活动及其整个视图树
解决方案:活动和视图
- 不惜一切代价避免对视图或活动进行静态引用
- 对活动上下文的所有引用都应该是短期的(函数的持续时间)
- 如果需要长期上下文,请使用应用程序上下文(
getBaseContext()或getApplicationContext())。它们不会隐式保留引用 - 或者,您可以通过覆盖配置更改来限制活动的销毁。但是,这并不能阻止其他潜在事件破坏活动。虽然您可以这样做,但您可能仍然希望参考上述实践
Runnables:简介
跑步其实没那么糟糕。我的意思是,他们可能是,但实际上我们已经到达了大多数危险区域。Runnable是一种异步操作,它从创建它的线程独立执行任务。大多数可运行程序都是从UI线程实例化的。本质上,使用Runnable是在创建另一个线程,只是稍微多管理一点。如果您像标准类一样将一个Runnable分类并遵循上面的指导原则,那么您应该不会遇到什么问题。事实上,许多开发人员并没有这样做
出于易用性、可读性和逻辑程序流的考虑,许多开发人员使用匿名内部类来定义其可运行性,如上面创建的示例。这将产生一个类似于您上面键入的示例。匿名内部类基本上是一个离散的内部类。您不必创建一个全新的定义,只需覆盖适当的方法。在所有其他方面,它都是一个内部类,这意味着它保持对其容器的隐式引用
可运行项和活动/视图
耶!这个部分可能很短!由于可运行程序在当前线程之外运行,因此这些操作的危险在于长时间运行的异步操作。如果在活动或视图中将runnable定义为匿名内部类或嵌套内部类,则存在一些非常严重的危险。这是因为,如前所述,它必须知道它的容器是谁。输入方向更改(或系统终止)。不