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().