一.概述
最近想研究一下Android中的内存泄露,今天我们先看看最简单而且比较容易发生的一种内存泄露情况,Context泄露,最近也看了几篇文章,下面分析一下:
先作个总结:
1.getApplicationContext()可以取到Application对象,而getContext()通常认为返回Activity对象(当然,事实上并不局限于Activity)。
2.对于Application,从Manifest文件中可以看出一个应用程序一般只有一个application节点。Application其实就是一个应用,即:当前应用程序只要还处于运行状态,那么就可以取到Application对象。
3.Application是一个长引用,Activity是短引用。Application适用于存储那些需要反复读取的对象,比如用户的用户名和密码,应用程序的当前设置等。Activity适用于当前活动窗体,比如显示一个dialog,或新建一个View,传入的context对象就应该是当前Activity,而非Application。
二.分析
1。CallBack对象的引用
先看一段代码
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);
}
我们重点看下面两行代码
TextView label = new TextView(this);
label.setBackgroundDrawable(sBackground)
我们看看setBackgroundDrawable(Drawable background)这个方法内部,有一句代码
background.setCallback(this);
这个this指的是什么呢?因为TextView继承自View,那么我们看看View的结构
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource
从这里我们就可以看出这个this代表的是Drawable.CallBack对象的引用,我们再看这两行代码
TextView label = new TextView(this);
label.setBackgroundDrawable(sBackground)
所以说sBackground持有View的引用,View持有Activity的引用,当一个Drawable绑定到了View上,实际上这个View对象就会成为这个Drawable的一个callback成员变量,上面的例子中静态的sBackground持有TextView对象lable的引用,而lable只有Activity的引用,而Activity会持有其他更多对象的引用。sBackground生命周期要长于Activity。当屏幕旋转时,Activity无法被销毁,这样就产生了内存泄露问题。
然后我们去看看Drawable中setCallBack的定义,在Android3.0之前的版本是这样写的。
public final void setCallback(Callback cb) {
mCallback = cb;
}
从Android3.0之后,谷歌引入弱引用解决了这个问题
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}
2。单例引起的内存泄露
public class Util {
3.
4. private Context mContext;
5. private static Util sInstance;
6.
7. private Util(Context context) {
8. this.mContext = context;
9. }
10.
11. public static Util getInstance(Context context) {
12. if (sInstance == null) {
13. sInstance = new Util(context);
14. }
15. return sInstance;
16. }
17.
18. //other methods
19. }
在初始化Util类的时候,我们传入了一个context对象,当Activity的生命周期结束的时候,我们的Util类里面还有一个Activity的引用,这样Activity占用的内存就不能被回收,造成了内存泄露,那么如何解决这个问题呢?可以用Util.getInstance(getApplicationContext()); 或Util.getInstance(getApplication()); 代替。
因为Application的生命周期是贯穿整个程序的,所以Util类持有它的引用,也不会造成内存泄露问题
总的来说,要避免Context相关的内存泄露,铭记以下几条:
•不要对Activity(Activity继承自Context)作长期的引用(一个指向Activity的引用与Activity本身有相同的生命周期);
•(如果使用长引用)试着用Application代替Activity;
•如果你不能控制内部类的生命周期,避免使用非静态内部类,应该用静态内部类,并且对里面的Activity作弱引用。该问题的解决方法是:对于外部类,用WeakReference构造静态内部类,同时要在视图根完成,并且它的WeakReference内部类要有一个实例(WeakReference)。
•垃圾回收不是防止内存泄露的保险方式。