Android内存泄露之Context

来源:互联网 发布:怎样查网卡的mac地址 编辑:程序博客网 时间:2024/05/22 03:23

一.概述

最近想研究一下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)。
•垃圾回收不是防止内存泄露的保险方式。

0 0
原创粉丝点击