网上很多教程,解决Handler造成的内存泄漏问题,基本上是使用弱引用来解决的,但是使用弱引用真的有效么?直接写代码演示,以及分析。
-
下面分析过程分为4种,第一种是Handler声明成静态的并且弱引用Activity。第二种是handler声明成成员变量的,使用弱引用。第三种是将handler声明成静态的。第四种是声明成普通成员变量,但是在onDestory中移除未执行完的任务。
- 第一种 Handler声明成静态的并且弱引用Activity
-
下面是代码
` public static class UIHndler extends android.os.Handler{ WeakReference<MainActivity> softReference; public UIHndler(MainActivity mainActivity){ softReference=new WeakReference<MainActivity>(mainActivity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity mainActivity = softReference.get(); if(mainActivity!=null){ // 更新ui mainActivity.handler(); } } }`
- 这种是我们经常写的代码,也是网上很多教程这样写的
- 下面是执行前和执行后的效果图,我们横竖屏切换来完成这个操作。
- 这张图是Activity启动的图
- 可以看到,初始申请内存是3.6m左右。接下来 我们反复的横竖屏切换,拿到下图
- 发现内存没有太多,接下来我们运行下GC,看到下图
- 发下确实没有造成内存泄漏,但是这样能说是弱引用的原因么?因为我们的Handler声明成静态的了
-
接下来我们演示第二种情况。handler声明成成员变量的,使用弱引用
-
下面是代码
public class UIHndler extends android.os.Handler{ WeakReference<MainActivity> softReference; public UIHndler(MainActivity mainActivity){ softReference=new WeakReference<MainActivity>(mainActivity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity mainActivity = softReference.get(); if(mainActivity!=null){ // 更新ui mainActivity.handler(); } } }
- 接下来我们还是横竖屏切换,看看内存申请
-
* 接下来我们Gc
* 发现内存并没有少,这是因为在 hanlder中有延迟任务。那么等延迟任务执行完了之后我们在GC下。下图等延迟任务执行万之后的是GC效果
-
发现也没有造成内存泄漏,但是得等到handler中的任务都执行完成之后才会清除内存。这样的话性能还是比较低,当然也没有造成内存泄漏。
- 接下来演示第三种情况 将handler声明成静态的
-
下面是我们的代码
public static android.os.Handler handler=new android.os.Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); tv.setText("dsa"); } };
- 也是反复的横竖屏切换
- 接下来gc
- 发现只要handler声明称static的就不会造成内存泄漏,而且回收很快
那么通过上面的三种情况我们发现。只要是handler声明成static就不会造成内存泄漏,声明成弱引用activity的话,虽然也不会造成内存泄漏,但是需要等到handler中没有执行的任务后才会回收,因此性能不高。
handler之所以造成内存泄漏是因为在activity销毁的时候,handler中有未执行完的任务。那么接下来我们在Activity销毁的时候清空handelr没有执行的任务会是什么效果
-
接下来我们看第四种情况
-
handler代码如下
public static android.os.Handler handler=new android.os.Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); tv.setText("dsa"); } };
-
但是我们在activity的onDestory中添加了如下代码,清空所有handler中没有执行完的任务
protected void onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null); }
- 下面是反复横竖屏内存图
-
- 下面是GC之后的
- 发现也没有内存泄漏
总结
- handler造成内存泄漏是因为在Activity销毁的时候还有未执行完的任务
- 静态static可以解决内存泄漏
- 使用弱引用也可以解决内存泄漏,但是需要等到handler的中任务都执行完,才会释放activity内存,不如直接static释放的快
- handler造成内存泄漏有 两种方案:一种是业务逻辑上,在activity销毁的时候移除所有未执行的任务。一种是从GC上,通过static的Handler或者弱引用解决。但是单独的使用弱引用性能不是太高。
static成员变量:
Java类提供了两种类型的变量:用static关键字修饰的静态变量和不用static关键字修饰的实例变量。静态变量属于类,在内存中只有一个复制,只要静态变量所在的类被加载,这个静态变量就会被分配空间,因此就可以被使用了。对静态变量的引用有两种方式,分别是“类.静态变量"和”对象.静态变量"
实例变量属于对象,只有对象被创建后,实例变量才会被分配内存空间,才能被使用,它在内存中存在多个复制,只有用“对象.实例变量”的方式来引用。