Android开发之—内存泄露篇
众所周知,手机开发,内存相当宝贵。至少在当前的T-Moble G1上,手机只有16M的内存可用,与PC应用开发的内存,真是天壤之别啊。因此如何规避手机应用开发内存泄漏问题,是手机应用开发的重中之重。
在开发过程当中,大部分内存泄漏的原因是,持有上下文引用的长周期对象,要知道,java的GC机制,只对无引用的对象才采取回收措施的。
在Andorid中,上下文对象context被用来做很多操作,但绝大多数是用来加载和访问资源文件。这就是为什么每个部件的构造函数都持有这个上下文对象作为入参。
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
setContentView(label);
}
这段代码意味着,TextView持有Activity的引用,那么它也将持有所以Activity持有对象的引用,通常是整个视图层级和所有的资源。因此如果你一直保持着与Activity的引用,那么GC将不会进行回收,最终导致内存泄漏。如果你不小心,在开发当中很容易出现这种情况。
在手机屏幕旋转时,系统默认销毁当前的Activity并创建一个新的Activity。这样做的结果是,Android将重新加载系统资UI源文件。假设你写的一个程序,有一个较大的bitmap,你不希望每次屏幕旋转时都加载这个图片,那么你将定义成static变量,如:
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附属在一个TextView对象上,View被设定为drawable上的一个回调。因此,drawable持有TextView的引用,而TextView持有Activity的引用,这样导致轮流关联引用到任何对象。
改进方法:在Activity的onDestory方法中,setCallBack为null。
@Override
protected void onDestroy() {
super.onDestroy();
unbindDrawables(findViewById(R.id.RootView));
System.gc();
}
private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
总结:避免内存泄漏的两大方针。
1、 避免引用当前上下文作用域之外的对象,就像以上例子的static对象在onCreate方法被引用一样。同样,内部类被外部类引用也存在同样的危险;
2、 使用Application上下文对象。这个对象长期存在,而不依赖于Activity的生命周期。如果你打算保留一个需要上下文Context对象的长期对象,那么建议你使用Application对象,获取Application对象的方法, Context.getApplicationContext() 或者Activity.getApplication().