避免内存泄露

        原文连接:点击打开链接, 翻译如下,初次翻译,希望列位批评指正。

        Android应用程序堆最大为16MB,至少在G1之前是这样(博主补充:Droid为24MB,Nexus One为32MB,Xoom为48MB)。对于手机而言已经是很大的内存空间了,但对于部分开发者而言还是很少的。即便没有将这些内存用完的打算,开发者也应尽量减少内存开销以便其他应用能够在后台运行而不会被强制关闭。这样的话,Android在内存中保存的应用越多,用户在应用间的切换就越快。我工作的一部分就是深入探索Android应用程序的内存泄露问题,大部分时间里,这些问题都是源自同一个错误:对Context(上下文环境)的长时间引用。

        在Android上,Context用于多种操作,但最多的还是用来加载和访问资源。这也是为什么所有的Widges在其构造函数中都有一个Context参数。常规的Android应用中,有两类Context:Activity Context和Application Context,通常前者被开发者传递给需要Context的类和方法。

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  setContentView(label);
}
这就意味着那些视图引用了整个Activity及其所拥有的一切:一般是整个视图层和所有资源。因此,如果泄露了这类Context(这里的泄露指的是引用Context,从而阻止了GC(垃圾回收)操作),就泄露了很多内存空间。如果不小心,泄露整个Activity是非常容易的事。

        在进行屏幕方向改变的时候,系统默认做法是保持状态不变的情况下,销毁当前Activity并重新创建一个新的Activity。这样做,Android会从资源文件中重新装载当前应用的UI。现在假设你写了一个带有很大一幅位图的应用,但你不想在每次屏幕旋转时都装载一次位图,最简单的做法就是将其保存在一个静态区域中:

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连接到一个视图上时,视图被设置为Drawable上的一个回调,在上面的代码片段中,这就意味着Drawable引用了TextView,而TextView又引用了Activity Context,Activity Context又进一步引用了更多的东西(依赖与你的代码)。

        上面这段示例是最简单的泄露Activity Context的情况,你可以到Home Screen's Source Code查看unbindDrawables()方法中看看我们是如何通过在Acitivity销毁时将存储Drawable的回调置为null来解决该问题的。如果再有兴趣的话,某些情况下会产生一个由泄露的Context形成的链,这很糟糕,会很快使得内存耗尽。

        有两种方法可以避免Activity Context相关的内存泄露:最明显的一种是避免在Activity Context自身的范围之外对其进行引用。上面这段示例展示了静态引用的情况,但对内部类和外部类的隐式引用同样都是危险的。第二种解决方法是用Application Context,因为该Context与应用的生命周期一样长,并不依赖Activity的生命周期。如果想拥有一个生命期足够长的object(对象),但却需要给其一个必须的Context的话,别忘了Application Object。获取Application Context的方法很简单:执行Context.getApplicationContext()或Activity.getApplication()。

        总之,为了避免Activity Context相关的内存泄露,记住下面几条:

1)、不要长时间引用一个Activity Context(引用周期应与Acitivity的生命周期一样长)

2)、尝试使用Application Context代替Acitivity Context

3)、在Activity中,避免使用你无法控制其生命周期的非静态的内部类,使用静态的内部类,并对Activity内部进行弱引用。就是在静态的内部类中对外部类进行弱引用,就如在ViewRoot及其W内部类中的做法那样

4)、垃圾回收(GC)无法保证内存泄露

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值