内存泄露就是指该被GC垃圾回收的,因为有另一个对象仍然在引用它,致使没法回收,形成内存泄露,过多的内存泄露会致使OOM。html
android中的内存泄露一般是Activity或者Fragment的泄露。下文分析以Activity展开,Fragment同理。java
1. 非静态内部类、匿名内部类
非静态内部类、匿名内部类 都会持有外部类的一个引用,若是有一个静态变量引用了非静态内部类或者匿名内部类,致使非静态内部类或者匿名内部类的生命周期比外部类(Activity)长,就会致使外部类在该被回收的时候,没法被回收掉,引发内存泄露, 除非外部类被卸载(JVM自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载,除非使用自定义的类加载器,感兴趣的同窗能够研究一下)。android
解决办法:
将非静态内部类、匿名内部类 改为静态内部类,或者直接抽离成一个外部类。
若是在静态内部类中,须要引用外部类对象,那么能够将这个引用封装在一个WeakReference中。以下面代码所示:
程序员
2. 静态的View
有时,当一个Activity常常启动,可是对应的View读取很是耗时,咱们能够经过静态View变量来保持对该Activity的rootView引用。这样就能够不用每次启动Activity都去读取并渲染View了。这确实是一个提升Activity启动速度的好方法!可是要注意,一旦View attach到咱们的Window上,就会持有一个Context(即Activity)的引用。而咱们的View有事一个静态变量,因此致使Activity不被回收。web
解决办法:
在使用静态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,若是在回调方法里有耗时操做,可能会形成内存泄露。
svg
解决办法:
在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实现的简单例子,咱们仍然没有使用弱引用。