Android应用开发之避免内存泄露

来源:互联网 发布:linux下ftp服务器搭建 编辑:程序博客网 时间:2024/05/16 07:43

传送门 ☞ Android兵器谱 ☞转载请注明 ☞ http://blog.csdn.net/leverage_1229

        Android应用至少是运行在内存限制为16MB的G1手机上。这些内存对手机来说已经足够了,但对某些开发者来说还远远不够。尽管不打算耗尽这些内存,但应该尽可能少地使用内存,以免把其他运行中的程序杀死(由于内存不足)。对用户来说,内存中保留的程序越多,在应用之间切换的速度就越快。在工作中,我遇到过很多的Android应用中都存在内存泄露,这些问题大多数都是出自于相同的原因:保持一个Context(上下文)的长期引用。

        在Android平台上,一个Context(上下文)能够被用来做很多事,但最常用的还是加载资源。这就是为什么所有的Widget(控件,例如TextView、EditText等),在它们的构造函数中要接收一个Context参数,在一个Android应用中,通常能使用两种Context,即Activity和Application。一般来说,当开发人员需要一个Context时,使用的是Activity(活动)。

    protected void onCreate(Bundle state) {        super.onCreate(state);        TextView label = new TextView(this);        label.setText("Leaks are bad");        setContentView(label);    }

        这就意味着,这些View控件对整个Activity有一个引用,随之,也就对Activity中的所有事物都有了引用;通常引用的事物是整个视图层级结构以及它所有的资源。因此,如果你leak了Context(“leak”的意思是,你对Context有一个引用,那么这就阻止垃圾回收器将它回收),那么,你就泄漏了许多的内存。如果你不当心一些的话,泄漏整个Activity是相当容易发生的。

        当屏幕的方向发生改变的时候,系统将默认地销毁当前的Activity,并创建一个新的Activity,在这个销毁与创建的过程中,同时保存了它的状态,如果这么做,Android将重新加载应用的所有UI资源,现在想象一下,你在写一个应用,你需要使用一个Bitmap,但你不想再每次屏幕旋转的时候都要重新加载一遍。那么,最简单的方法就是把它保存在一个静态域中:        
    private static Drawable sBackground;    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对象的一个callback(回调)。在上面的代码片段中,这个Drawable对象对TextView有一个引用,而这个TextView对这个Activity也有一个引用,可怕的是,这个Activity对很多东西都有引用(这取决于你的代码)。

        这是一个最简单的内存泄漏的例子,可以在HomeScreen源码中,看一看我们是如何解决这个问题的(查找unbindDrawables()方法),这就是当Activity被销毁的时候将drawables的回调设为null。有趣的是,你可能创造出一系列Context泄漏的情况有很多,这非常糟糕,它们将很快导致内存溢出。
        有两种简单的方法来避免Context相关的内存泄漏。最显著地一个是避免Context溢出它自己的范围之外。上面的例子展示的是使用静态引用的情况。然而,内部类以及它们对其外部类的隐式引用也是同样危险的。第二种解决方法是使用Application Context。这个Context的生命周期和你的应用的生命周期一样长,而不是取决于Activity的生命周期。         如果你想保持一个需要Context的长期对象,记得使用Application对象。你可以通过调用Context.getApplicationContext()或Activity.getApplication()来获取应用对象。
        总而言之,想要避免与Context相关的内存泄漏,记住以下几点:
        (1)要保持对activity-context的长期引用(activity引用的生命周期应该和activity自身的生命周期相同)。
        (2)试着使用application-context替代activity-context。
        (3)如果不能控制Activity中非静态内部类的生命周期,那么使用静态内部类,并在其中对Activity进行WeakReference(弱引用)。解决这个问题的方法是使用静态内部类,并且对它的外部类有一个弱引用。例如ViewRoot中内部类W所做那样。
        (4)垃圾回收器不是处理内存泄漏的保障。
原创粉丝点击