Android 应用程序被限制最多使用16MB的堆空间,至少在T-Mobile G1上是这样。这对电话而言是很多空间,但对目前程序员想要得到的而言却很少的可怜。哪怕你没有打算使用这些给定的空间。你也应该尽可能的少用而留给其他应用程序更多的空间以免他们挂了。Android在内存中能存储越多的应用,用户在应用间的切换速度也就越快。作为我工作的一部分,我研究了一下Android应用程序的内存泄漏问题,发现他们大部分源于同一个错误:保留了一个长时间生存的上下文Context的引用。
在Android上,许多操作都要用到Context,但大部分只是为了读取和访问资源。这就是为什么所有的Widgets都在构造函数Constructor中获得一个Context变脸作为输入参数。在一个普通的Android应用中,你通常有两种Context,活动Activity和应用Application.通常是程序员传递给那些需要Context的类或者方法的都是第一种Context:
1
2
3
4
5
6
7
8
9
|
@Override
protected
void
onCreate(Bundle state) {
super
.onCreate(state);
TextView label =
new
TextView(
this
);
label.setText(
"Leaks are bad"
);
setContentView(label);
}
|
这意味着视图Views有一个整个Activity的引用也包括这整个Activity所包含的东西,通常是整个视图层次何所有它的资源。如果你泄漏了Context(泄漏 是指你保持一个引用并不让GC(垃圾回收机制)回收它)。你就会泄漏很多内存。如果你不小心一点那泄漏整个Activity都不是难事。
当屏幕的方向发生变化,系统默认会摧毁当前的Activity然后创建一个适合当前状态的新的Activity。Android将从资源中重新读取应用程序的界面UI来完成这个操作。现在想象你写了一个含有很大Bitmap的应用程序,你也不希望每次转动手机他都会重新读取一遍。最简单的方法就是把它设置在静态区域:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
private
static
Drawable sBackground;
@Override
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对象被连接在视图上,这个视图就被设成drawable的一个回调.这对于上面的代码意味着,drawable有一个TextView的引用,而该TextView本身有个Activity的引用(Context),而这个引用依次又引用了几乎所有的东西(视具体代码而定).
这个例子是最简单的泄露Context的案例,你可以看看在Home screen’s source code里看看我们是如何通过在Activity被摧毁时设置drawable的回调函数为NULL来避开这个问题(查阅unbindRawables()方法)。还有很多例子展示你在什么地方可以创建一连串的泄露Contexts,而且他们都很有破坏性,他们能让你很快的用完内存。
这有两种简单的方法来避免和Context有关的内存泄露。最显然的方法是避免使Context跑到他自己的范围之外。以上的例子展示了一个静态引用但是内部类和他们对外部类的隐式引用也可能同样危险。第二个解决方法是使用Application的Context。这个Context的寿命和你的应用程序一样长而且不依赖于Activity的生命周期。如果你计划保持一个需要Context的长寿命对象,记住可以通过Context.getApplicationContext()或者Activity.getApplication()来使用Application Context。
总之,为了避免于Context有关的内存泄露,记住以下几点:
- 不要保持一个长寿命的Activity的Context的引用(一个Activity的引用的寿命应该和这个Activity一样长)
- 试试使用Application Context代替Activity Context
- 如果你不想控制他们的生命周期,避免在一个Activity内使用非静态的内部类,使用静态的内部类并且使用对Activity的弱引用。这个情况的做法是使用一个含有到外部的WeakReference的静态的内部类。就好像在ViewRoot和他的W内部类一样。
- 垃圾处理机制不是制服内存泄露的终极保险