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

举报
程思扬 发表于 2022/01/13 23:17:18 2022/01/13
【摘要】 在进行异步操作时,我们经常会使用到Handler类。常见的写法如下。 public class MainActivity extends Activity {       ......   &n...

在进行异步操作时,我们经常会使用到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回收,即出现内存泄漏。

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

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

  1. public class MainActivity extends Activity {  
  2.     ......  
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         ......  
  8.   
  9.         new Thread(new Runnable() {  
  10.             @Override  
  11.             public void run() {  
  12.                 HttpUtils.downloadFile(url);  
  13.                 mHandler.sendEmptyMessage(MSG_SUCCESS);  
  14.             }  
  15.         }).start();  
  16.     }  
  17.   
  18.     private Handler mHandler = new Handler() {  
  19.         @Override  
  20.         public void handleMessage(Message msg) {  
  21.             if (msg.what == MSG_SUCCESS) {  
  22.                 // your code  
  23.             }  
  24.         }  
  25.     };  

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

解决方法主要在于两点:
1.将Handler声明为静态内部类。因为静态内部类不会持有外部类的引用,所以不会导致外部类实例出现内存泄露。
2.在Handler中添加对外部Activity的弱引用。由于Handler被声明为静态内部类,不再持有外部类对象的引用,导致无法在handleMessage()中操作Activity中的对象,所以需要在Handler中增加一个对Activity的弱引用。

改善之后的代码如下。

  1. public class MainActivity extends Activity {  
  2.     private final MyHandler mHandler = new MyHandler(this);  
  3.   
  4.     private static class MyHandler extends Handler {  
  5.         private final WeakReference<MainActivity> mActivity;  
  6.   
  7.         public MyHandler(MainActivity activity) {  
  8.             this.mActivity = new WeakReference<MainActivity>(activity);  
  9.         }  
  10.   
  11.         @Override  
  12.         public void handleMessage(Message msg) {  
  13.             MainActivity mainActivity = mActivity.get();  
  14.             if (mainActivity == null) {  
  15.                 return;  
  16.             }  
  17.             // your code here  
  18.         }  
  19.     }  

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

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


文章来源: chengsy.blog.csdn.net,作者:程思扬,版权归原作者所有,如需转载,请联系作者。

原文链接:chengsy.blog.csdn.net/article/details/79375442

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。