Android开发之内存泄漏
前言
最近感觉很迷茫了不知道干啥,入职差不多一周了,每次下班回家就不想打开电脑了,学习动力大不如以前了,不过我还是尽量的在下班时间多学点东西。
内存泄漏也是 Android 开发和面试时很重要的一个点,在咱们网站搜“内存泄漏”还是有一些相关的搜索的。
以下内容是我在B站看视频的笔记,希望对同学们预防和解决内存泄漏问题有帮助~
单例模式
使用单例模式时很容易导致内存泄漏,因为单例模式的生命周期往往与 Application 的生命周期是一致的,如果你往该单例里传入了一个生命周期较短的对象(如:Activity)时,这个对象(Activity)就会一直被这个单例所持有,如果(Activity)一直被持有就会导致内存泄漏。所以我们在使用单例的时候一定要小心,如果非要传入一个上下文时一定要思考清楚,它的上下文的生命周期的长度是怎样的。
静态变量
值得注意的是静态的生命周期是非常长的,大概与应用的 JVM 存在的生命周期是一样的,也就是说和 Application 存在的时间是一样的,所以我们在使用静态变量的时候,一定要考虑清楚。能不用静态变量就不要用静态变量,如果非得用,那么你得思考这个静态变量它到底会占用我们使用过程中的多少内存,是否有其它的替代方案。当然这也是我们的一些编程细节问题。
Handler内存泄漏
Handler 背后有一个持有链,线程、loop、MessageQueue、Message和Handler本身。如果你的 Handler 是以匿名内部类的方式定义的,那么这个匿名内部类就会持有 Activity ,然而我们在发送 Message 的时候,我们的 Message 又会持有 Handler ,而 Message 又会被 MessageQueue 所持有,而 MessageQueue 又被 loop 持有, loop 又是对应一个线程的一个死循环,所以它是不会被退出的,那么这个时候如果你的 Message 不退出,Message 不执行,那就意味着它持有的 Handler 不会释放,Handler 又持有了 Activity ,Activity 就会这样被泄漏了。所以使用 Handler 的时候,如果直接用匿名内部类的 Handler 会有可能导致内存泄漏,这一点在写代码时 AndroidStudio 会给我们提示,但很多人还是不会去注意。(无奈ing~)
匿名内部类
匿名内部类它会持有我们外部类对象,所以你得考虑它可能会带来内存泄漏的问题。如果你在匿名内部类里面进行了一个长时间的运行,比如说在匿名内部类里进行了一个耗时操作,那么这个时候匿名内部类就不会退出,这个匿名内部类不退出,它所持有的 Activity 上下文也就不会退出,那么这样带来的后果就是内存泄漏。所以当我们使用匿名内部类的时候,一定要注意这个匿名内部类是否持有了外部类对象,而外部类是否又持有了一些其它的类,比如说 Activity 持有的一个类,然后这个 Activity 又被另外一个类持有,在另外一个类里面又存在一个匿名的内部类。(这个可能会有点小绕,得慢慢去理解~)
资源的释放
在 Android 里面所讲的资源,比如说:数据库的打开是否有关闭,文件(File)打开是否有关闭,流打开是否有关闭,Bitmap 是否有释放。这一系列的资源往往都会设计缓存机制(为了用户体验~),但这种缓存机制往往不是 JVM 可以解决和自动释放的,因为它会涉及到底层 C/C++ 的释放,所以我们需要对这一系列需要释放的资源在生命周期结束的时候(如 Activity、Fragment的onDestroy、onPause方法里面)对资源进行释放,尤其是音视频播放,释放资源就是释放内存的一个非常重要的点。这是由于 Android 特性里面的一系列可能导致内存泄漏的点。
注册、反注册
注册广播、注册EventBus,这些都需要我们unRegister(注销、反注册)。
Context
第三方的库经常会要求传入 Context(上下文),Context 意味着把 Fragment、Activity、Application所有的上下文资源全部传给了这些第三方库,如果这些第三方库没有自动的释放这些库,那我们的 APP 就会产生内存泄漏。所以当我们需要往第三方库传入 Context 的时候,我们需要传入一个比较准确的 Context 对象,一定要注意这个 Context 的生命周期是在什么时候有用,什么时候应该释放。所以我们在使用的时候一定是取短不取长,这是原则,哪怕有可能会带来NPE(NullPointException:空指针异常)问题,但这个问题是第三方SDK不严谨导致的。如果第三方的SDK没有明确告诉你需要传入一个什么样的 Context ,那么在这种情况下尽量使用 Activity 的 Context 。(是尽量而不是全部~)
ArrayList、LinkedList、HashMap...
比如说用 ViewPager 来持有一系列的 Fragment ,那么这一系列的 Fragment 就会被 Activity 所持有,因为 Activity 通过 ArrayList(或者其它的,此处只是简单举个栗子~) 来持有 Fragment ,如果 ArrayList 不被你释放,那么我们的 Fragment 就会一直被持有。虽然你调用了它的生命周期结束,但是它仍然被 Activity 所持有的 ArrayList 所持有,所以当我们需要使用 ArrayList 的时候,强烈建议不要将这个内存释放的操作交给 JVM 去完成,一定要慎重的去思考 ArrayList、LinkedList或者HashMap它们应该在什么时候对数据进行 clear ,如果能够在 onDestroy 方法里面进行 clear 的话,一定要自己在 onDestroy 方法中手动的进行数据的 clear 。