Handler引起的内存泄漏及解决方法

简介: Handler引起的内存泄漏及解决方法

在进行异步操作时,我们经常会使用到Handler类。常见的写法如下。


public class MainActivity extends Activity {       ......          private Handler mHandler = new Handler() {           @Override           public void handleMessage(Message msg) {           }       };   }

当使用内部类或匿名内部类的方式创建Handler时,Handler对象会隐式地持有一个外部类对象的引用(这里的外部类是Activity)。一般在一个耗时任务中会开启一个子线程,如网络请求或文件读写操作,我们会使用到Handler对象。但是,如果在任务未执行完时,Activity被关闭了,Activity已不再使用,此时由GC来回收掉Activity对象。由于子线程未执行完毕,子线程持有Handler的引用,而Handler又持有Activity的引用,这样直接导致Activity对象无法被GC回收,即出现内存泄漏。


所以这段代码有可能会引起内存泄漏。


下面用一段代码示例来说明。


public class MainActivity extends Activity {  

   ......  

 

   @Override  

   protected void onCreate(Bundle savedInstanceState) {  

       super.onCreate(savedInstanceState);  

       ......  

 

       new Thread(new Runnable() {  

           @Override  

           public void run() {  

               HttpUtils.downloadFile(url);  

               mHandler.sendEmptyMessage(MSG_SUCCESS);  

           }  

       }).start();  

   }  

 

   private Handler mHandler = new Handler() {  

       @Override  

       public void handleMessage(Message msg) {  

           if (msg.what == MSG_SUCCESS) {  

               // your code  

           }  

       }  

   };  

}

在MainActivity启动时,开启一个子线程来下载文件。如果文件较大或网络不稳定的因素,导致短时间内无法执行完成,用户按下返回键退出了当前界面。此时子线程仍然在运行,并持有mHandler的引用,而mHandler是一个匿名内部类的对象,持有MainActivity的引用,这样MainActivity对象无法被回收,MainActivity内部的很多资源都无法被回收。

解决方法主要在于两点:

1.将Handler声明为静态内部类。因为静态内部类不会持有外部类的引用,所以不会导致外部类实例出现内存泄露。

2.在Handler中添加对外部Activity的弱引用。由于Handler被声明为静态内部类,不再持有外部类对象的引用,导致无法在handleMessage()中操作Activity中的对象,所以需要在Handler中增加一个对Activity的弱引用。


改善之后的代码如下。


public class MainActivity extends Activity {  

   private final MyHandler mHandler = new MyHandler(this);  

 

   private static class MyHandler extends Handler {  

       private final WeakReference<MainActivity> mActivity;  

 

       public MyHandler(MainActivity activity) {  

           this.mActivity = new WeakReference<MainActivity>(activity);  

       }  

 

       @Override  

       public void handleMessage(Message msg) {  

           MainActivity mainActivity = mActivity.get();  

           if (mainActivity == null) {  

               return;  

           }  

           // your code here  

       }  

   }  

}

关于WeakReference的说明:

WeakReference即弱引用,与强引用(即我们常说的“引用”)相对。它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向,该对象就会在被GC检查到时回收掉。


对于上面在子线程中下载文件的示例代码,改为采用WeakReference之后,如果任务未执行完但是用户却关闭了MainActivity,但由于仅有一个来自MyHandler的弱引用指向MainActivity,所以GC仍然会在检查时把MainActivity回收掉。这样,内存泄露的问题就不会出现了。


目录
相关文章
|
2月前
|
Java 程序员 C++
深入探讨内存泄漏的原因及解决方法
深入探讨内存泄漏的原因及解决方法
|
12月前
win10桌面窗口管理器进程内存占用大解决方法
win10桌面窗口管理器进程内存占用大解决方法
595 0
|
5天前
|
Java
Handler内存泄漏原因及解决方案
Handler内存泄漏原因及解决方案
11 0
|
9月前
|
存储 缓存 JavaScript
什么是javascript内存泄漏?以及解决方法
内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。 简单理解:无用的内存还在占用,得不到释放和归还,比较严重的时候,无用的内存还会增加,从而导致整个系统卡顿,甚至崩溃
142 0
一次性讲清楚 Handler 使用不当导致的内存泄露?
一次性讲清楚 Handler 使用不当导致的内存泄露?
一次性讲清楚 Handler 使用不当导致的内存泄露?
|
Android开发
关于安卓Handler内存泄漏及解决方案
Handler是安卓中常见的实现异步操作的方法,使用简单,但是操作不注意很容易造成内存泄漏。
124 0
|
缓存 关系型数据库 数据库
GitLab内存占用过高的解决方法
GitLab内存占用过的高解决方法: 系统环境:CentOS 7 GitLab版本:12.10.2-ee 服务器配置:2核4G 非转载以实践
9143 0
GitLab内存占用过高的解决方法
|
存储 Web App开发 监控
JS高程中的垃圾回收机制与常见内存泄露的解决方法
前言 起因是因为想了解闭包的内存泄露机制,然后想起《js高级程序设计》中有关于垃圾回收机制的解析,之前没有很懂,过一年回头再看就懂了,写篇博客与大家分享一下。如果喜欢的话可以点波赞/关注,支持一下。 个人博客了解一下:obkoro1.com 内存的生命周期: 分配你所需要的内存: 由于字符串、对象等没有固定的大小,js程序在每次创建字符串、对象的时候,程序都会分配内存来存储那个实体。 使用分配到的内存做点什么。 不需要时将其释放回归: 在不需要字符串、对象的时候,需要释放其所占用的内存,否则将会消耗完系统中所有可用的内存,造成系统崩溃,这就是垃圾回收机制所存在的意义。
160 0
JS高程中的垃圾回收机制与常见内存泄露的解决方法
|
算法 Java Kotlin
小题大做 | Handler内存泄露全面分析
嗨,大家好,问大家一个“简单”的问题:Handler内存泄露的原因是什么?
184 0
小题大做 | Handler内存泄露全面分析