道客优

1234
Android性能优化之内存泄漏
2019-03-02 刀刻油 阅读:951

  

内存管理的目的就是让我们在开发过程中有效避免我们的应用程序出现内存泄露的问题。内存泄露相信大家都不陌生,我们可以这样理解:「没有用的对象无法回收的现象就是内存泄露」。

如果程序发生了内存泄露,则会带来以下这些问题

  • 应用可用的内存减少,增加了堆内存的压力

  • 降低了应用的性能,比如会触发更频繁的 GC

  • 严重的时候可能会导致内存溢出错误,即 OOM Error

在Android中以下几种情况一般会引起内存泄漏

 1、   静态变量引起的泄漏
 2、  单例模式引起的泄漏
 3、   非静态内部类持有外部引用导致的泄漏
 4、   Handler引起的内存泄漏
 5、   属性动画引起的泄漏

静态变量导致的内存泄漏

这种情况常见的是Context的使用,比如我们写了一个工具类,里面的静态方法需要用到Context。如果我们将Activity的this传给这个方法,那么Activity在被回收的时候由于这个静态变量持有Activity的引用,导致不能被回收从而引起内存泄漏。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layoyt_memory);
      AppUtil.getTesLeak(this);
    }
    
    public static void getTesLeak(Context context) {
        Toast.makeText(context, "您的内存泄漏啦", Toast.LENGTH_SHORT).show();
    }

解决上面的问题也很多简单,如果我们的工具类不是一定需要Activity的Context,难么我们可以考虑使用getApplicationContext()。因为 getApplicationContext() 是和我们App的生命周期一样长,如果App不退出他就不会被回收。
单例模式导致的内存泄漏
单例引起的内存泄漏,大概思路上面差不多,也是因为静态变量的生命周期太长,如果程序不退出,系统就不会对其回收,这将导致本应该不用的对象不能回收,我们可以指定Context为getApplicationContext();来避免内存泄漏。

    public class MemorySingle {
        //如果传入上下文
        private static Context context;
        private MemorySingle() {
        }
    
        public static MemorySingle getInstance(Context context) {
            //防止内存泄漏
            MemorySingle.context = context.getApplicationContext();
            return Menory.single;
        }
        
        static class Menory {
            private static final MemorySingle single = new MemorySingle();
        }
    }

非静态内部类持有外部引用引起的泄漏
因为非静态内部类的生命周期是和外部类的生命周期绑定在一起的,非静态内部类会持有外部类的引用,如果我们在内部类中做一些耗时操作,如下面内部类sleepThread()方法让线程睡10秒,在这个时候如果Activy要销毁,但是因为内部类持有外部类的引用,它的sleepThread()方法还没执行完,所以导致Activy不能被回收,引起内存泄漏。

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layoyt_memory);
        TestLeak testLeak = new TestLeak();
        testLeak.sleepThread();
    }
     class TestLeak {
        private void sleepThread() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //睡10秒
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

解决方法将TestLeak改成静态内部类

    static class TestLeak {
        private void sleepThread() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //睡10秒
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

Handler引起的内存泄漏
我们使用Handler做消息处理的时候可能不注意会用下面这种写法:

    private Handler mHanlder = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 200:
                    mTV_incloud_merge.setText((String) msg.obj);
                    break;
            }
            super.handleMessage(msg);
        }
    };

上面的mHanlder是Handler的非静态匿名内部类,上面我们提到过非静态匿名内部类会持有外部引用,所以如果使用上面的写法也会引起内存泄漏。下面有两种方式可以避免泄漏。
第一种方式: 使用静态内部类+弱引用方式

    static class MyHanlder extends Handler {
        //弱引用
        WeakReference<Activity> mWeakRef;
        public MyHanlder(Activity activity) {
            mWeakRef = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 200:
                    LayoutMemoryActivity activity = (LayoutMemoryActivity) mWeakRef.get();
                    activity.mTV_incloud_merge.setText((String) msg.obj);
                    break;
            }
        }
    }


第二种: Handler.Callback方式处理消息

Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case 200:
                mTV_incloud_merge.setText((String) msg.obj);
                break;
        }
        return false;
    }
})


复制代码属性动画导致的内存泄漏
当一个Activy中有一个无限循环的属性动画,在Activy销毁的时候没有停止动画也会引起内存泄漏

     ObjectAnimator oA = ObjectAnimator.ofFloat(mBnt_layoutMemory, "rotation", 0, 360).setDuration(20000);
                   oA.start();

上面的是一个按钮选装动画,20秒后执行完,如果在动画还为执行完的时候销毁Activy,将会导致Activy无法释放引起内存泄漏。下面是解决办法

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消动画
        oAnimator.cancel();
    }
 

推荐阅读: