前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发:详解Handler的内存泄露

Android开发:详解Handler的内存泄露

作者头像
Carson.Ho
发布2019-02-22 15:05:07
1K1
发布2019-02-22 15:05:07
举报
文章被收录于专栏:Android知识分享Android知识分享

前言

  • 内存泄露在Android开发中非常常见
    1. 内存泄露的定义:本该被回收的对象不能被回收而停留在堆内存中
    2. 内存泄露出现的原因:当一个对象已经不再被使用时,本该被回收但却因为有另外一个正在使用的对象持有它的引用从而导致它不能被回收。 这就导致了内存泄漏。
  • 本文将详细讲解内存泄露的其中一种情况:在Handler中发生的内存泄露 阅读本文前建议先阅读Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)

目录

目录
目录

1. 背景

我们先来看下日常Handler的一般用法:

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
    //主线程创建时便自动创建Looper和对应的MessageQueue,之前执行Loop()进入消息循环
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    //实例化Handler
    //这里并无指定Looper,即自动绑定当前线程(主线程)的Looper和MessageQueue
        private Handler showhandler = new Handler(){
        //通过复写handlerMessage()从而决定如何进行更新UI操作
        @Override
        public void handleMessage(Message msg) {
        //UI更新操作
            }
    };
    //启动子线程
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                 showhandler.sendEmptyMessageDelayed(0x1,10000);

                }
            }
        }.start();

      finish();
}

在上面的例子中,你会发现出现了严重的警告:

Paste_Image.png
Paste_Image.png

从上图可以看出来,这个警告的原因是:该Handler造成了严重的内存泄漏

那么,该Handler是怎么样造成内存泄露的呢?

2. 内存泄露原因

2.1 造成内存泄露的源头

根据图片可以分析,内存泄露显示出现在:

  • Handler类 即Handler四件套:Looper+MessageQueue+Message+Handler
  • 最终的泄露发生在Handler类的外部类 - MainActivity类
2.2 如何造成内存泄露

首先,我们需要了解到:

  • 主线程的Looper对象会伴随该应用程序的整个生命周期
  • 在Java里,非静态内部类匿名类都会潜在引用它们所属的外部类

在了解到上述两条后,从上面的代码中可以知道:

  • 在发送的延迟空消息(EmptyMessageDelayed)后、消息处理被前,该消息会一直保存在主线程的消息队列里持续10s
  • 在这延时10s内,该消息内部持有对handler的引用,由于handler属于非静态内部类,所以又持有对其外部类(即MainActivity实例)的潜在引用,引用关系如下图
引用关系
引用关系
  • 这条引用关系会一直保持直到消息得到处理,从而,这阻止了MainActivity被垃圾回收器(GC)回收,同时造成应用程序的内存泄漏,如下图:
泄露分析
泄露分析

3. 解决方案

3.1 解决方案1:使用静态内部类+弱引用

上面提到,在Java里,非静态内部类匿名类都会潜在的引用它们所属的外部类。 但是,静态内部类不会。

所以,避免内存泄露的解决方案是:只需要将Handler的子类设置成静态内部类

  • 同时,还可以加上 使用WeakReference弱引用持有Activity实例
  • 原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

解决代码如下:

代码语言:javascript
复制
public class MainActivity extends AppCompatActivity {


//将Handler改成静态内部类
     private static class FHandler extends Handler{
      //定义弱引用实例
      private WeakReference<Activity> reference; 

      //在构造方法中传入需要持有的Activity实例
      public MyHandler(Activity activity) { 
      reference = new WeakReference<Activity>(activity); }

        //通过复写handlerMessage()从而决定如何进行更新UI操作
        @Override
        public void handleMessage(Message msg) {
        //省略代码
            }
}

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    //主线程创建时便自动创建Looper和对应的MessageQueue,之前执行Loop()进入消息循环
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    //实例化Handler的子类
    //这里并无指定Looper,即自动绑定当前线程(主线程)的Looper和MessageQueue
        private final Handler showhandler = new FHandler();

         //启动子线程
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                 showhandler.sendEmptyMessageDelayed(0x1,10000);

                }
            }
        }.start();
    }
3.2 解决方案2:当外部类结束生命周期时清空消息队列
  • 从上面分析,内存泄露的原因是: 当Activity结束生命周期时,Handler里的Message可能还没处理完,从而导致一系列的引用关系。
  • 其实,我们只要在当Activity结束生命周期时清除掉消息队列(MessageQueue)里的所有Message,那么这一系列引用关系就不会存在,就能防止内存泄露。
  • 解决方案:当Activity结束生命周期时(调用onDestroy()方法),同时清除消息队列里的所有回调消息(调用removeCallbacksAndMessages(null))

代码如下:

代码语言:javascript
复制
@Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

经过上述两个解决方案,在Handler里的内存泄露问题就不会再出现了!


4. 总结

  • 本文总结的是关于Handler的一些小事:内存泄露,阅读完本文后相信你已经懂得Handler内存泄露的原理和详细的解决方案
  • 接下来,我会继续讲解Android开发中关于Handler和多线程的知识,包括Handler源码、继承Thread类、实现Runnable接口、Handler等等,有兴趣可以继续关注Carson_Ho的安卓开发笔记

请点赞!因为你的鼓励是我写作的最大动力!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年09月28日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 目录
  • 1. 背景
  • 2. 内存泄露原因
  • 3. 解决方案
  • 4. 总结
  • 请点赞!因为你的鼓励是我写作的最大动力!
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档