防止android应用的内存泄露

来源:互联网 发布:自学php需要什么基础 编辑:程序博客网 时间:2021/09/25 17:32
body {font-family: arial, helvetica,sans-serif;color: black;background-color: #fff;font-size: 10.5pt;margin: 1em;padding: 1em;line-height: 1.5;}table {empty-cells: show;}p {padding: 0;}h1, h2, h3, h4, h5 {padding: 0;}h1 {font-size: 1.3em; /*1.3em*/}h2 {font-size: 1.1em; /*1.3em*/}h3 {font-size: 1em; /*1.1em;*/;line-height: 1.4em;white-space: nowrap;padding: 0;}sup, sub {font-size: .7em;}input, select, textarea, option {font-family: inherit;font-size: inherit;}img {border-style: none;}a {outline: none;color: #00c;}a:active {color: red;}a:visited {color: #551a8b;}hr {border: 0;background-color: #eee;border-bottom: #c9d7f1 1px solid;height: 0px;width: 99.9%;text-align: left;margin-top: 1em;margin-bottom: 1em;}
防止android应用的内存泄露

        android应用一般——至少在T-Mobile G1上面——被限制在16M的堆内存上运行。它对于一部手机来说可能已经绰绰有余了,但同时对一些开发者来说却是不够的。尽管你没有打算用掉这全部的内存,但是还是要考虑要让你自己的应用占用尽量少的内存,从而让其它和你的应用同时运行的应用不会因为内存紧张而被系统kill掉——否则我们的应用就是不道德的、不慷慨的。所以,在我平时的开发过程中,一个重要的工作就是去检查自己的代码有没有造成自己的android应用发生内存泄露(Memory Leaks)。总结后我发现,一大部分的android应用内存泄露问题的产生都是源于一个原因:给Context对象保持了长久存在的引用。
        在Android中,一个Context别用于很多事情,但最常起到的作用还是索引上下文中的资源。这就是每个widget(TextView、Button)都在产生新实例时在它们的构造器中传入Context的原因。一般在一个Android应用中,我们有两种Context——Activity和Application。而我们开发者常常传入给需要Context变量的方法或者类的便是前者——一个Activity示例的引用:
@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  setContentView(label);
}
        这就意味着view保存了一个它所处的activity的引用(可以推想的是, activity所保有的其他对象也是如此——包括这个activity所拥有的View整体层级以及相关资源)。从此看来,如果一个activity类型的Context在内存中被泄露了的话,被涉及到的内存量是相当可观以及可怖的。但是可怕的是,如果不注意的话,泄露一整个activity是非常容易发生的。
        当屏幕的方向发生变化时,在默认情况下系统将销毁掉当前的activity然后从新生成一个新的。这么做的话,Android将重新从资源里获取activity的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对象被附属到一个view上时,这个view将被设置为该Drawable一个回调,在刚才的那段代码中,也就会有drawable有一个TextView的引用,而同时TextView又存在一个针对它所处的activity的引用(也就是我们方才所说的Context),这样一来也就导致了这个activity因为总是存在一个引用而导致其无法被垃圾回收。而如果你的这个activity又同时对某些其它的资源进行了引用保存,那更不幸的内存泄露将会发生。
        方才那段代码是一个简单的泄露Context对象的例子。
        要防止泄露Context的悲剧发生,我们有两种手段。我们都比较明了的一个是拒绝让activity类型的context脱离它本身的作用域从而防止像刚才那段代码中展示的无法消除的引用导致的无法垃圾回收。第二种手段是用Application类型的Context对象。这个Context对象会在你的应用存在的时期中一直存在下去——正如它的名字所预示的一样——它不会受activity生命周期的影响。如果你想要保留一个长久存在的需要Context对象的对象,别忘了使用Application Context。你可以简单地通过调用 Context.getApplicationContext()或者 Activity.getApplication()来获得Application Context对象。
    避免Context类型的内存泄露,请务必记得如下几条:
          •     1.不要将一个生命周期计划将超过Activity生命周期的对象通过引用保存的方式绑定到一个Activity类型Context上去。如果要绑定,被绑定的对象一定是和该activity拥有相同生命周期的。

    2.尝试着在保存长生命周期的对象时用Application Context来代替Activity Context。

    3.避免在一个activity中使用一个你没有控制它生命周期的非static的内部类,而是使用static的内部类,然后在里面需要使用context的地方使用外部activity的弱引用(而非方才代码中的强引用)。

    4.记住:垃圾回收机制对于内存泄露来说并不是可以迷信的保障。