Android中常见的内存泄露及解决办法汇总

内存泄露就是指该被GC垃圾回收的,由于有另外一个对象仍然在引用它,导致无法回收,造成内存泄露,过多的内存泄露会导致OOM。

  android中的内存泄露通常是Activity或者Fragment的泄露。下文分析以Activity展开,Fragment同理。

1. 非静态内部类、匿名内部类
2. 静态的View
3. Handler
4. 监听器(各种需要注册的Listener,Watcher等)
5. 资源对象没关闭造成内存泄漏
6. 属性动画
7. RxJava
8. WebView
9. 其他的系统控件以及自定义View
10. 不一定非要使用弱引用才行
1. 非静态内部类、匿名内部类
  非静态内部类、匿名内部类 都会持有外部类的一个引用,如果有一个静态变量引用了非静态内部类或者匿名内部类,导致非静态内部类或者匿名内部类的生命周期比外部类(Activity)长,就会导致外部类在该被回收的时候,无法被回收掉,引起内存泄露, 除非外部类被卸载(JVM自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载,除非使用自定义的类加载器,感兴趣的同学可以研究一下)。

解决办法: 
  将非静态内部类、匿名内部类 改成静态内部类,或者直接抽离成一个外部类。 
如果在静态内部类中,需要引用外部类对象,那么可以将这个引用封装在一个WeakReference中。如下面代码所示: 


2. 静态的View
  有时,当一个Activity经常启动,但是对应的View读取非常耗时,我们可以通过静态View变量来保持对该Activity的rootView引用。这样就可以不用每次启动Activity都去读取并渲染View了。这确实是一个提高Activity启动速度的好方法!但是要注意,一旦View attach到我们的Window上,就会持有一个Context(即Activity)的引用。而我们的View有事一个静态变量,所以导致Activity不被回收。

解决办法: 
  在使用静态View时,需要确保在资源回收时,将静态View detach掉。

3. Handler
  我们知道,主线程的Looper对象不断从消息队列中取出消息,然后再交给Handler处理。如果在Activity中定义Handler对象,那么Handler肯定是持有Activty的引用。而每个Message对象是持有Handler的引用的(Message对象的target属性持有Handler引用),从而导致Message间接引用到了Activity。如果在Activty destroy之后,消息队列中还有Message对象,Activty是不会被回收的。当然了,如果消息正在准备(处于延时入队期间)放入到消息队列中也是一样的。

解决办法: 
  将Handler放入单独的类或者将Handler放入到静态内部类中(静态内部类不会持有外部类的引用)。如果想要在handler内部去调用所在的外部类Activity,可以在handler内部使用弱引用的方式指向所在Activity,这样不会导致内存泄漏。 
  或者在onDestory时,调用相应的方法移除回调和删除消息。 


4. 监听器(各种需要注册的Listener,Watcher等)
  当我们需要使用系统服务时,比如执行某些后台任务、为硬件访问提供接口等等系统服务。我们需要把自己注册到服务的监听器中。然而,这会让服务持有 activity 的引用,如果程序员忘记在 activity 销毁时取消注册,那就会导致 activity 泄漏了。 
  例如:EditText的一个addTextChangeListener,如果在回调方法里有耗时操作,可能会造成内存泄露。 


解决办法: 
  在onDestory时,取消注册,editText.removeTextChangedListener

5. 资源对象没关闭造成内存泄漏
  当我们打开资源时,一般都会使用缓存。比如读写文件资源、打开数据库资源、使用Bitmap资源等等。当我们不再使用时,应该关闭它们,使得缓存内存区域及时回收。虽然有些对象,如果我们不去关闭,它自己在finalize()函数中会自行关闭。但是这得等到GC回收时才关闭,这样会导致缓存驻留一段时间。如果我们频繁的打开资源,内存泄漏带来的影响就比较明显了。

解决办法: 
  及时关闭资源

6. 属性动画
  在使用ValueAnimator或者ObjectAnimator时,如果没有及时做cancel取消动画,就可能造成内存泄露。

  因为在cancel方法里,最后调用了endAnimation(); ,在endAnimation里,有个AnimationHandler的单例,会持有属性动画对象的引用,如下代码所示; 


解决办法: 
  在在onDestory时,调用动画的cancel方法

7. RxJava
  在使用RxJava时,如果在发布了一个订阅后,由于没有及时取消,导致Activity/Fragment无法销毁,导致的内存泄露 
解决办法: 
  参考Uber出品的一个开源库AutoDispose的使用,可以参考下文: 
  Android架构中添加AutoDispose解决RxJava内存泄漏

8. WebView
  在android 5.1及以上版本的代码中,WebView可能会存在内存泄露, 
  原因可以参考这篇文章:Android 5.1 WebView内存泄漏问题及解决

解决办法: 
  在销毁webview前一定要onDetachedFromWindow,我们先将webview从它的父view中移除再调用destroy方法,代码如下: 


9. 其他的系统控件以及自定义View
在 Android Lollipop 之前使用 AlertDialog 可能会导致内存泄漏 
参考:一个内存泄漏引发的血案 
Dialog和DialogFragment在Android5.0以下的内存泄漏 
参考:解决Android5.0以下Dialog引起的内存泄漏 
View的post方法导致的内存泄漏分析

view中有线程或者动画 要及时停止

这是为了防止内存泄漏,可以在onDetachedFromWindow方法中结束,这个方法回调的时机是 当View的Activity退出或者当前View被移除的时候 会调用 这时候是结束动画或者线程的好时机 另外还有一个对应的方法 onAttachedToWindow 这个方法调用的时机是在包含View的Activity启动时 回调 回调在onDraw方法 之前

10. 不一定非要使用弱引用才行
如避免AsyncTask内存泄漏的简单例子: 
 
这里是AsyncTask: 


当然这个例子非常基础,但是我认为作为另一种解决方案的演示来说足够了。 
这里是另一个使用RxJava实现的简单例子,我们仍然没有使用弱引用。 

--------------------- 
作者:unicorn97 
来源:CSDN 
原文:https://blog.csdn.net/unicorn97/article/details/81009204 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值