Android 内存泄漏优化

    对于 Android 开发来说,有一个问题是我们必须要重视的:内存泄漏。因为内存泄漏是造成应用程序 OOM 的主要原因之一。对于内存泄漏,我们可以在开发过程中尽量避免,也可以使用 Android Studio 自带的分析工具或者 MAT 来找出潜在的内存泄漏加以解决。

    那什么是内存泄漏呢?内存泄漏是指在程序开发中,当一个对象不再需要使用,本该被回收时,却被另一个正在使用的对象持有它的引用,导致这个不需要的对象不能被回收而停留在堆内存中,产生了内存泄漏。

    那么有哪些情况会造成内存泄漏呢?

  • 静态变量导致的内存泄漏

    静态变量是一个全局变量,当静态变量含有对一个对象的引用时,就会导致这个对象在不需要时不能被回收。如下:

public class MainActivity extends AppCompatActivity {
    private static Context context;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = this;
    }
}

    当全局变量 context 持有了对 MainActivity 的引用,会导致在不需要 MainActivity 时,而不能回收它,所以我们要避免这种写法。我们可以使用生命周期更长的 ApplicationContext 来给 context 赋值。 

  • 非静态内部类创建静态实例导致内存泄漏

    非静态内部类默认持有外部类的引用,而又有一个该内部类的全局变量,就会导致当不需要这个外部类时,导致不能被回收。如下:

public class MainActivity extends AppCompatActivity {
    private static Res res = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (res != null){
            res = new Res();
        }
    }
    class Res{}
}

    其实这种情况和上一种情况比较类似,这种情况的话一般我们可以把非静态内部类改为静态内部类、或者将内部类抽取出来封装成一个单例模式。

  • 单例模式导致内存泄漏

    Android 有很多设计模式,单例模式便是其中之一,非常受开发者喜爱,不了解单例模式的人可以看一下:单例模式,稍微了解一下。因为单例模式的静态特性会使得单例模式的生命周期和应用的生命周期一样长,如果单例模式持有对某个对象的引用,导致该对象无法被回收,造成内存泄漏。这个的例子代码就不写了,因为其实和上面两种情况也比较类似,都是因为静态变量引用对象导致内存泄漏。

  • Handler 导致内存泄漏
public class MainActivity extends AppCompatActivity {
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //处理事务
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Message message = new Message();
        message.arg1 = 0;
        handler.sendMessage(message);
    }
}

    上面的 handler 是 Handler 的非静态匿名内部类的实例,会持有外部类 MainActivity 的引用,当 MainActivity 退出时如果 Handler 的消息队列中还有未处理或者正在处理的消息,就会导致 MainActivity 不能被回收,导致内存泄漏。

public class MainActivity extends AppCompatActivity {
    private static class MyHandler extends Handler {
        //使用弱引用
        private WeakReference<Context> reference;
        public MyHandler(Context context) {
            reference = new WeakReference<Context>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = (MainActivity) reference.get();
            if (activity!=null){
                //处理事务
            }
        }
    }
    private MyHandler handler = new MyHandler(this);
    private static final int MESSAGE = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyHandler handler = new MyHandler(this);
        Message message = new Message();
        message.arg1 = 0;
        message.arg2 = MESSAGE;
        handler.sendMessage(message);
    }
    // 当 Activity 退出时,即在 Activity 的 onDestroy() 方法里,清空所有消息
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //清空所有消息
        handler.removeCallbacksAndMessages(null);
        //也可清除特定消息
        handler.removeMessages(MESSAGE);
    }
}

    我们可以使用静态内部类和弱引用来避免内存泄漏,并在 MainActivity 退出时清空循环机制里面所有的消息。不熟悉弱引用的小伙伴可以看看这一篇:Java 四大引用

  • 使用多线程导致内存泄漏

    在 Java 和 Android 中多线程一定是经常用到的,比如为了防止 Android 的 ANR,就会把一些耗时操作,如网络请求和文件操作等,放入子线程中进行,然后利用线程通信来完成对主线程的一些操作。如下:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                //进行操作     
            }
        }).start();
    }
}

    上述代码有一个问题就是 Runnable 是实现了一个匿名内部类,会对 MainActivity 有一个隐式引用,如果 MainActivity 退出前,子线程任务没有完成,对 MainActivity 的引用会导致 MainActivity 无法被回收造成内存泄漏。这种情况就可以使用静态内部类:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new MyRunnable()).start();
    }
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            //进行操作
        }
    }
}
  • 属性动画导致内存泄漏

    属性动画作为 Android 三大动画之一,其使用之简单以及对属性操作的强大功能导致它十分受 Android 开发者的喜爱,有着很重要的位置。但是如果使用属性动画做了一些无限循环的动画,导致动画一直持有 Activity 的 View 的引用,而 Activity 又持有 View 的引用,导致 Activity 无法被回收,还有就是动画一直循环对于  Android 资源也是一种消耗。

    这时候我们只需要判断动画的需求,在 Activity 或者 Fragment 的 onStop() 或者 onDestroy() 方法里面调用属性动画的 cancel() 方法就可以取消动画。

  • 资源未关闭导致内存泄漏

    在 Android 开发中,我们不可避免的会使用一些资源,比如说动态注册的广播(BroadcastReceiver)呀,输入输出流(Stream)呀,文件(File)呀,图片(Bitmap)相关的呀,Cursor 等等。

    这个时候我们就需要记得在不使用的时候回收或者关闭它们,一般是在某个生命周期里(看应用需求)或者是在 try-catch 的 finally (必定会执行)里。

猜你喜欢

转载自blog.csdn.net/young_time/article/details/80324056