Android 避免内存泄漏的两大方针

Android开发之—内存泄露篇

众所周知,手机开发,内存相当宝贵。至少在当前的T-Moble G1上,手机只有16M的内存可用,与PC应用开发的内存,真是天壤之别啊。因此如何规避手机应用开发内存泄漏问题,是手机应用开发的重中之重。
 
在开发过程当中,大部分内存泄漏的原因是,持有上下文引用的长周期对象,要知道,java的GC机制,只对无引用的对象才采取回收措施的。
 
在Andorid中,上下文对象context被用来做很多操作,但绝大多数是用来加载和访问资源文件。这就是为什么每个部件的构造函数都持有这个上下文对象作为入参。
 
@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  setContentView(label);
}
 
这段代码意味着,TextView持有Activity的引用,那么它也将持有所以Activity持有对象的引用,通常是整个视图层级和所有的资源。因此如果你一直保持着与Activity的引用,那么GC将不会进行回收,最终导致内存泄漏。如果你不小心,在开发当中很容易出现这种情况。
 
在手机屏幕旋转时,系统默认销毁当前的Activity并创建一个新的Activity。这样做的结果是,Android将重新加载系统资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附属在一个TextView对象上,View被设定为drawable上的一个回调。因此,drawable持有TextView的引用,而TextView持有Activity的引用,这样导致轮流关联引用到任何对象。
 
改进方法:在Activity的onDestory方法中,setCallBack为null。
@Override 
    protected void onDestroy() { 
    super.onDestroy();  
    unbindDrawables(findViewById(R.id.RootView)); 
    System.gc(); 
    } 
 
    private void unbindDrawables(View view) { 
        if (view.getBackground() != null) { 
        view.getBackground().setCallback(null); 
        } 
        if (view instanceof ViewGroup) { 
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { 
            unbindDrawables(((ViewGroup) view).getChildAt(i)); 
            } 
        ((ViewGroup) view).removeAllViews(); 
        } 
    } 
 
 
总结:避免内存泄漏的两大方针。
1、  避免引用当前上下文作用域之外的对象,就像以上例子的static对象在onCreate方法被引用一样。同样,内部类被外部类引用也存在同样的危险;
 
2、  使用Application上下文对象。这个对象长期存在,而不依赖于Activity的生命周期。如果你打算保留一个需要上下文Context对象的长期对象,那么建议你使用Application对象,获取Application对象的方法, Context.getApplicationContext()  或者Activity.getApplication().

  

posted @ 2013-06-22 19:04  沉静至善  阅读(135)  评论(0编辑  收藏  举报