美文网首页首页投稿(暂停使用,暂停投稿)android实用技术Android
Android Context导致的内存泄漏分析(示例代码+分析

Android Context导致的内存泄漏分析(示例代码+分析

作者: Mr云台 | 来源:发表于2016-10-26 12:10 被阅读0次

    Android开发中因为有限的内存,以及防止OOM问题出现,解决内存泄漏问题将是开发者一直持续下去的工作。本文就分析了不当使用(持有)context导致的内存泄漏。

    1. 为什么使用Context有可能会导致内存泄漏?

    首先从context的本质谈起,context名称上代表了上下文,实质上是Application、Activity或Service的一个引用。因此如果有生命周期较长的对象,比如线程持有了一个context引用,那么在线程结束前,这个context是无法得到释放的,这也意味着context代表的activity、service无法被GC回收,这就发生了内存泄漏。

    2. 来个例子

    还记得屏幕旋转,会销毁当前Activity,重新创建一个新的Activity的事吗?我们就以这个操作来做泄漏内存的示例,原理是让老的Activity无法被销毁。

     private static Drawable mBackgroudImage;
    
        @Override
        protected void onCreate(Bundle state) {
            super.onCreate(state);
    
            TextView label = new TextView(this);//持有context
            label.setText("演示旋转屏幕导致内存泄漏");
    
            if (mBackgroudImage == null) {
                mBackgroudImage = getDrawable(R.drawable.beauty); //这个是随便在网上找的一张图片
            }
            label.setBackgroundDrawable(mBackgroudImage);
    
            setContentView(label);
        }
    
    

    好,运行起来,将屏幕旋转几次,这个代码已经泄漏了老的Activity。


    image

    是不是内心有点小怀疑?

    我们往下看截图可以证明。

    3. 使用Android Monitor验证内存泄漏情况

    怎么验证,是否发生了内存泄漏呢?

    我们可以通过Android Studio提供的Memory Monitor工具来观察Java Heap情况。

    打开Android Monitor,如图所示,我们旋转几次屏幕后,点击“Dump Java Heap”按钮

    image

    接下来会,弹出一个hprof编辑框,在右边的红圈部分点开“分析界面”


    image

    好,在新弹出的分析界面,点击绿色箭头开始分析,下半部分就是分析结果。


    image

    注意我红圈标注出来的地方,发现SplashActivity竟然有2个实例!!!!这就证明确实发生了内存泄漏,有一个SplashActivity泄漏了。

    4. 分析原因。

    • 首先我们发现,这段代码定义了一个没有初始化的静态Drawable变量。众所周知,静态变量是属于一种跟“类”而非“实例”绑定在一起的对象。是一个被所有实例共享的成员变量,当给它赋值的时候,实际上是赋值给了整个“类对象”。

    • 出于省事的考虑,代码中并没有在每次onCreate中去加载赋值mBackgroudImage,而是检测它如果不为空,就不再赋值。

    • 根据官网的说法,将一个Drawable对象赋给某个View的时候,这个View同时也作为一个callBalk被Drawable对象给引用了。即,Drawable对象持有对应View的引用。这个可以看setBackgroundDrawable()源码,确实是没错的。

    @Deprecated
        public void setBackgroundDrawable(Drawable background) {
               //省略其他内容,可以看到确实View
               //作为一个callBalk被Drawable对象给引用了
        
               // Set callback last, since the view may still be initializing.
                background.setCallback(this);
                }
    
    • 事实上,当我们第一次运行起来SplashActivity时,会给mBackgroudImage赋值,当屏幕旋转的时候则不会第二次赋值。因此,mBackgroudImage仍旧持有第一次的TextView的引用。

    • 而TextView的新建需要传入一个context,因此它持有了SplashActivity的一个引用。

    • 所以,相当于静态变量mBackgroudImage间接的、始终持有第一个创建出来SplashActivity没有释放。

    • 所以就发生了内存泄漏!!!

    总结

    通过本文的分析,让一个生命周期长于Activity的对象持有context引用很容易就发生了内存泄漏,开发者需要特别警惕!

    相关文章

      网友评论

        本文标题:Android Context导致的内存泄漏分析(示例代码+分析

        本文链接:https://www.haomeiwen.com/subject/cxvduttx.html