本站已关停,现有内容仅作科研等非赢利用途使用。特此声明。
查看: 4711|回复: 2
打印 上一主题 下一主题

[Android分享]Context引起的内存泄露分析

[复制链接]
跳转到指定楼层
1#
发表于 2013-3-22 10:12:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 Andy.Ma 于 2013-3-23 23:11 编辑

    在开发Android App中,Context几乎无处不在。如果用不好Context很可能就会引起内存泄露。

    在Android中,Context用于许多操作,但最主要是加载和访问资源。这就是Android所有的UI控件的构造方法中需要接收一个Context参数的原因。在常规的Android App中,通常主要有两种类型的Context,分别是Activity和Application。
    我们看一段代码片段:
@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);

  TextView label = new TextView(this);
  label.setText("Leaks test");

  setContentView(label);
}

    上面代码中的向label传入了一个this,意味着label持有了整个Activity。也就是说持有了整个该Activity的View hierarchy和资源。所以如果泄露了Context,将会泄露很多内存。如果你不小心,泄露整个Activity是很容易出现的。

    注意,这里所讲的Context泄露是指:一个对象保持并引用了一个Context,使得GC无法回收它。

    默认的,当屏幕方向改变时,当前的Activity将会销毁并且会创建一个新的Activity,新创建的Activity将会保持之前的状态。在这种情况下,Android系统会从资源中从新加载UI。假设你正在编写一个Activity,该Activty将加载一个大的位图( a large bitmap),但是你不想每次屏幕方向改变时都加载它。最简单的办法是创建一个静态的成员变量来引用它,看下面代码片段:

private static Drawable sBackground;

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);

  TextView label = new TextView(this);
  label.setText("Leaks test");

  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);

  setContentView(label);
}

    以上代码执行会很有效率,但是存在很大一个问题:当屏幕方向改变时,首次创建的Activity将会被泄露。当一个Drawable依附在View上时,Drawable会使用setCallback保留对该View的引用。从上面代码可以看出:sBackground 将会引用label,而label又持有整个Activity(Context)资源。也就是说当屏幕改变方向时,sBackground不被回收,他引用的一系列资源也就回收不掉。
    以上代码只是一个Context泄露的例子,如果想解决上面这个泄露的问题,就需要在Activity的onDestory方法中调用sBackground.setCallback(null),来解除sBackground 对label的引用。

    有两种方法可以避免Context相关的内存泄露。最明显的一种方法是:避免context逃离自己的范围。上面的例子是静态引用Context,同样的静态引用非静态的内部类也同样危险,因为非静态内部类隐式的引用外部类。
    第二种方法就是用Application Context,Application Context对于整个应用APP来说是全局的,它不依靠Activity的生命周期。如果你需要保持一个长期的对象并且需要Context,记住要用Application Context.通过调用Context.getApplicationContext()和 Activity.getApplication(),你可以很方便的获得Application Context。

    总之,为了避免Context相关的内存泄漏,请记住以下几点:
    (1).不要保持一个长期的Activity的Context引用。一个Activity的引用必须与该Activity的生命周期相同。
    (2).试着用Application Context来代替Activity Context。
    (3).如果你不控制非静态的内部类的生命周期,请不要在Activity中定义非静态的内部类。可以使用静态内部类,并且使用弱引用( WeakReference)来引用Activity(Context).
    (4).垃圾回收器(GC)不能阻止内存泄露。

ChinaGDG.com
回复

使用道具 举报

2#
发表于 2013-3-26 13:22:46 | 只看该作者
访问系统接口、访问应用资源,都可以通过Application Context来访问,Activity Context只是一个当前环境的快捷方式,一旦写超出当前界面的代码,要么传入Activity Context作为参数“借用”,要么就用Application Context来引用。
楼主的写的很到位。
ChinaGDG.com
回复 支持 反对

使用道具 举报

3#
发表于 2013-7-26 12:49:28 | 只看该作者
楼主写得很好,挺不错的
ChinaGDG.com
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表