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().