[摘]Handler内存泄露原理及解决方法

Android  Java  2020年7月9日 pm12:54发布4年前 (2020)更新 城堡大人
142 0 0

SHUI知道

本想总结,也懒了,看网上也有很多人总结,也就摘抄于此,以供自己查阅。

前言

因为Android采取了单线程UI模型,开发者无法在子线程中更新UI,为此Android为我们提供了Handler这个工具,可以开发者切换到主线程更新UI。

示例

首先看一段示例代码

public class LeakCanaryActivity extends AppCompatActivity

    private  Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

            }
        };

        Message message = Message.obtain();
        message.what = 1;
        mHandler.sendMessageDelayed(message,10*60*1000);
    }

}

这段代码的逻辑很简单,mHandler延时了10分钟发送消息,类似的代码在我们的项目中也经常出现,但是这样的代码会出现一个问题。

问题

我们在项目中集成 Square 的开源库 LeakCanary,有关这个库的介绍及使用请看:Github.LeakCanary

我们首先打开 LeakCanaryActivity ,然后按返回键将这个Activity finish 掉。等待几秒屏幕上会弹出提醒和通知,这说明此时发生了内存泄露的现象。

原因

究竟是什么时候发生了内存泄露的问题呢?

我们知道在Java中,非静态内部类会隐性地持有外部类的引用,二静态内部类则不会。在上面的代码中,Message在消息队列中延时了10分钟,然后才处理该消息。而这个消息引用了Handler对象,Handler对象又隐性地持有了Activity的对象,当发生GC是以为 message – handler – acitivity 的引用链导致Activity无法被回收,所以发生了内存泄露的问题。

危害

众所周知,内存泄露在 Android 开发中是一个比较严重的问题,系统给每一个应用分配的内存是固定的,一旦发生了内存泄露,就会导致该应用可用内存越来越小,严重时会发生 OOM 导致 Force Close。

解决

这个问题该如何解决呢?

使用弱引用

首先我们需要理解一下相关概念:

  • 强引用:

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

  • 软应用:

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

弱引用:

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

简介

用更直白的语言描述就是,java对于 强引用 的对象,就绝不收回,对于 软引用 的对象,是能不收回就不收回,这里的能不收回就是指内存足够的情况,对于 弱引用 的对象,是发现就收回,但是一般情况下不会发现。

很显然,出现内存泄露问提的原因,就是 Handler 对 Activity 是强引用,导致 GC 在回收 Activity 时无法回收。为了解决这个问题,我们可以把 Handler 对 Activity 弱引用,这样 GC 就能把 Activity 及时的回收,从而杜绝了内存泄露的问题。

public class NoLeakActivity extends AppCompatActivity {

    private NoLeakHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mHandler = new NoLeakHandler(this);

        Message message = Message.obtain();

        mHandler.sendMessageDelayed(message,10*60*1000);
    }

    private static class NoLeakHandler extends Handler{
        private WeakReference<NoLeakActivity> mActivity;

        public NoLeakHandler(NoLeakActivity activity){
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    }
}

运行项目,并没有发生内存泄露的问题。

及时清除消息

在原因中我们说到,正是因为被延时处理的 message 持有 Handler 的引用,Handler 持有对 Activity 的引用,形成了message – handler – activity 这样一条引用链,导致 Activity 的泄露。因此我们可以尝试在当前界面结束时将消息队列中未被处理的消息清除,从源头上解除了这条引用链,从而使 Activity 能被及时的回收。

public class LeakCanaryActivity extends AppCompatActivity {

    private  Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

            }
        };

        Message message = Message.obtain();
        message.what = 1;
        mHandler.sendMessageDelayed(message,10*60*1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

运行项目,也没有发生内存泄露的问题。

摘抄

  1. Handler内存泄露原理及解决方法

 历史上的今天

  1. 2023: Framework单独编译简介(0条评论)
  2. 2021: 余秀华:我爱你(0条评论)
  3. 2019: 余秋雨:我在等你(0条评论)
  4. 2019: 梁实秋:先生们的书房(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

lnmp安装记录

前言简单记录一下使用 LNMP一键安装包,以及自己遇到的问题,主要是方便自己查阅。这里只是简单记录,推荐看参考文。正文LNMP一键安装包LNMP一键安装包是一个用Linux Shell编写的可以为 CentOS / RHEL / Fedora / Debian / Ubuntu / Ra...

Bitmap转String进行数据库MySQL保存

前言解析音乐ID3信息时,需要在扫描时把专辑图解析出来,然后保存到数据中。本文就简单介绍一下Bitmap和String之间的转换。PS:20211113日更新指定转换的bitmap指定高宽。好记性不如烂笔头正文这里分为String转Bitmap和bitmap转String来说。隐藏内...

自定义垂直Seekbar简介

前言记录一下自定义的垂直SeekBar,记录于此,方便自己查阅。正文一般来说,水平SeekBar是最多的,比如多媒体中的进度控制,音量条的控制,但是偶尔存在项目需要竖直SeekBar。由于没有原生的,要么自定义,要么直接把SeekBar进行90°的翻转也可以,当然,网上也很多相关资料和开源...

Zygote的启动之一app_main

前言Android系统中,所有的应用程序以及SystemServer都是由Zygote进程孕育(fork)出来的。这里介绍app_main.cpp中的代码。Android P正文涉及文件\system\core\rootdir\init.rc\system\core\rootdir...

林清玄:路上的情书

我捡过一封诀别的情书。情书上有这样看来普通的句子:“当初是我选择了你,心里明知与你不会长久,还是执着的选择了你。”“这些日子以来,谢谢你陪我走过这一段路。”“你是一个很好的人,你一定会认识比我好上千倍的女孩。”“由衷地希望在没有我的日子,你依然过得好。”会捡到这封情书是很偶然的。有一天我在...

叶芝 : 当你老了

当你老了,头白了,睡意昏沉, 炉火旁打盹,请取下这部诗歌, 慢慢读,回想你过去眼神的柔和, 回想它们昔日浓重的阴影; 多少人爱你青春欢畅的时辰, 爱慕你的美丽,假意或真心, 只有一个人爱你那朝圣者的灵魂, 爱你衰老了的脸上痛苦的皱纹; 垂下头来,在红光闪耀的炉...