传送门 ☞ Android兵器谱 ☞转载请注明 ☞ http://blog.csdn.net/leverage_1229
Android应用至少是运行在内存限制为16MB的G1手机上。这些内存对手机来说已经足够了,但对某些开发者来说还远远不够。尽管不打算耗尽这些内存,但应该尽可能少地使用内存,以免把其他运行中的程序杀死(由于内存不足)。对用户来说,内存中保留的程序越多,在应用之间切换的速度就越快。在工作中,我遇到过很多的Android应用中都存在内存泄露,这些问题大多数都是出自于相同的原因:保持一个Context(上下文)的长期引用。
在Android平台上,一个Context(上下文)能够被用来做很多事,但最常用的还是加载资源。这就是为什么所有的Widget(控件,例如TextView、EditText等),在它们的构造函数中要接收一个Context参数,在一个Android应用中,通常能使用两种Context,即Activity和Application。一般来说,当开发人员需要一个Context时,使用的是Activity(活动)。
protected void onCreate(Bundle state) { super.onCreate(state); TextView label = new TextView(this); label.setText("Leaks are bad"); setContentView(label); }
这就意味着,这些View控件对整个Activity有一个引用,随之,也就对Activity中的所有事物都有了引用;通常引用的事物是整个视图层级结构以及它所有的资源。因此,如果你leak了Context(“leak”的意思是,你对Context有一个引用,那么这就阻止垃圾回收器将它回收),那么,你就泄漏了许多的内存。如果你不当心一些的话,泄漏整个Activity是相当容易发生的。
当屏幕的方向发生改变的时候,系统将默认地销毁当前的Activity,并创建一个新的Activity,在这个销毁与创建的过程中,同时保存了它的状态,如果这么做,Android将重新加载应用的所有UI资源,现在想象一下,你在写一个应用,你需要使用一个Bitmap,但你不想再每次屏幕旋转的时候都要重新加载一遍。那么,最简单的方法就是把它保存在一个静态域中:private static Drawable sBackground; protected void onCreate(Bundle state) { super.onCreate(state); TextView label = new TextView(this); label.setText("Leaks are bad"); if(sBackground == null) { sBackground = getDrawable(R.drawable.large_bitmap); } label.setBackgroundDrawable(sBackground); setContentView(label); }
这段代码运行起来相当快,但是也错的离谱。程序把屏幕第一次旋转时创建的Activity给泄漏了。当Drawable对象被加载到一个View上以后,这个View对象就被设置成为Drawable对象的一个callback(回调)。在上面的代码片段中,这个Drawable对象对TextView有一个引用,而这个TextView对这个Activity也有一个引用,可怕的是,这个Activity对很多东西都有引用(这取决于你的代码)。
这是一个最简单的内存泄漏的例子,可以在HomeScreen源码中,看一看我们是如何解决这个问题的(查找unbindDrawables()方法),这就是当Activity被销毁的时候将drawables的回调设为null。有趣的是,你可能创造出一系列Context泄漏的情况有很多,这非常糟糕,它们将很快导致内存溢出。