内存泄漏
内存泄漏是指由于疏忽或错误造成程序未能释放已经不再使用的内存的。在手机开发过程中,系统分配给应用的内存是有限的。如果应用所使用的内存超过了手机分配给的内存。就会造成 OOM(内存溢出)。因此我们在开发过程中要避免写出内存泄漏的代码。
常见的内存泄漏
- 单例持有 Context 对象 (单例的生命周期从创建对象开始到程序结束) ,如果传入 Activity 的 Context ,就会造成内存泄漏 .
解决方案 : 如果一定要使用 Context,可以考虑传入 Application 的 Context 。 - 静态变量导致的内存泄漏 。例如使用 static 来修饰 Activity 的 Context 。
- Handler 造成的内存泄漏 (Handler造成内存泄漏是因为在Activity销毁的时候还有未执行完的任务)
(1) 例如 : postDelayed(Runnable r, long delayMillis) , 延迟 10 s , finish() 掉 Activity ,此时匿名内部类持有外部类的引用,导致 Activity 不能被回收。
解决方案 : onDestory 的时候调用 Handler.removeCallbacksAndMessages(null) , 移除 Handler 中 所有的 Callback 和 Message 。这里注意如果
Handler 是在主线程初始化 ,那么移除的就是 主线程 MessageQueue 中的 Message, 有人会有疑问那这个方法岂不是很危险,如果有其他 Activity 在使用
Handler 做轮询 , 不用担心,移除 Message 的时候,会判断 msg.target 是否和要移除的 Handler 一致。
(2) 例如 : 子线程异步耗时任务,之后使用 Handler 切换线程,刷新UI。异步线程正在执行,此时 finsh() 掉 Activity ,那么仅仅调用 removeCallbacksAndMessages 是没 有 用的。因为此时 Message 还没有进入 MessageQueue 。
解决方案 : 造成内存泄漏的原因 ,还是匿名内部类或非静态内部类持有外部类的引用导致的。那么我们只要使用静态内部类或者单独放在一个类文件中。这样静 态
内部类不会持有外部类的引用。同时如果静态内部类调用外部的 Activity 时,直接使用弱引用来处理即可。 - AsyncTask 造成的内存泄漏
AsyncTask 内部封装了线程池和 Handler , 。异步执行任务后,使用 Handler 切换线程。凡是使用 Handler 的地方,必然要注意内存泄漏的问题。和上述的问题一 样,
AsyncTask 正在执行任务,此时 finish () 掉 Activity ,必然会有内存泄漏的发生 。
解决方案 : Android 4.0后 , AsyncTask 中默认是串行执行任务 当然你可以指定线程池来并行执行任务) 。 因此在 onDestory 的时候调用 AsyncTask的cancel()方 法。同时和 Handler 一样 ,创建 AsyncTask 的时候使用静态内部类,同时调用外部的 Activity 时,直接使用弱引用来处理即可。 - 属性动画造成的内存泄漏
在Activity 当中如果指定属性动画为无限循环并且在 onDestory 中没有停止动画,那么动画会一直循环下去,尽管在界面中已经无法看到动画效果了。
解决方案 : 在 Activity 的 onDestory 中调用 动画的 cancel() 方法来取消。
内存泄漏检测工具 :
(1) leakcanary 地址 : https://github.com/square/leakcanary 具体的使用方法,可看网站或者他人博客。
(2) Android Studio 3.0 以上的 Profile
(3) Eclipse 的 MAT
weak-handler
使用 Handler 觉得静态内部类和弱引用写起来麻烦可以使用 https://github.com/badoo/android-weak-handler
相关知识点 :
如果你对 Handler 或者 AsynctTask 的理解不深或者没有看过源码。那么我推荐你学习的顺序为 :
Handler -> HandlerThread -> IntentService -> AsyncTask
引用 : java 强应用、软引用、弱引用 、虚引用
推荐书籍
Android 开发艺术探索 第10章 和 第11章