性能优化--handler内存泄漏分析

本文通过实例展示了Android开发中因Handler使用不当引起的内存泄漏,利用AndroidStudio的Profiler和MAT工具进行内存泄漏分析,详细解释了内存泄漏的原因,并提供了三种解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开发中,如果代码不规范很容易产生内存泄漏,比如Handler、Context、线程等使用。本文使用Android Studio自带的Profiler和MAT工具进行内存泄漏分析。

一、内存泄漏

1、定义handler
private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            mHandler.sendEmptyMessage(0);
            return false;
        }
    });
2、在onCreate()方法调用
mHandler.sendEmptyMessage(0);

以上件简单大的几句代码就发生了内存泄漏,可以用profiler工具查看。

二、profiler

1、使用profiler运行程序

在这里插入图片描述
点击小图标即可

2、选择分析Memory

在这里插入图片描述
点击MEMORY一栏

3、按返回键退出当前页面,然后点击垃圾桶小图标

在这里插入图片描述

4、在回收后截取内存的一块

在这里插入图片描述

5、底部按包名查找,找到对应包名下的文件

在这里插入图片描述
Allocations:申请的大小
Deallocations:释放的大小
Total Count:对象引用次数
Shallow Size:对象自身占用的内存大小

这里重点关注第三个参数,我们已经退出了当前的MainActivity界面,但是发下对象的引用个数为1,说明它没有被回收。发生了内存泄漏。下面使用MAT工具,分析具体的内存泄漏原因。

三、MAT

1、点击如下图标:

在这里插入图片描述

2、将选中的内存保存下来,后缀为hprof

在这里插入图片描述

3、只分析内存部分,需要使用命令行对文件进行过滤

找到hprof-conv.exe所在的目录,一般在SDK目录下的platform-tools目录。截图如下:
在这里插入图片描述
把文件复制到这里。然后执行命令行,生成新的文件memory-2.hprof。命令行如下:
在这里插入图片描述

hprof-conv -z 当前文件 目标文件
4、用MAT工具打开当前文件

在这里插入图片描述
(1)点击Histogram。
在这里插入图片描述
(2)在这里可以输入你要分析的文件名称,由上面的Profile工具分析可以看出泄漏的文件为MainActivity。所以直接输入MainActivity,按回车,右击当前文件
在这里插入图片描述
排除掉所有的弱引用和软引用等。我们只分析强引用对象。
(3)展开引用,排除系统的引用。查看哪些外部对象引用它
在这里插入图片描述
(4)如下图
在这里插入图片描述
排除系统的,发现MainActiivty$2内部类仍然连接着原始对象this。说明this是一个根对象,Handler是与他相连的。导致它无法回收。

四、解决

(1)方式一:使用移除消息的方式
在onDestroy()方法中添加如下代码:

mHandler.removeMessages(0);

优点:能够解决内存泄漏。
缺点:如果我们需要在界面结束的时候,需要继续在后台完成一个操作。完成后需要Handler通知,那么这种方式就失效了。

(2)方式二:使用弱引用
代码如下:

private class MyHandler extends Handler {
        private WeakReference<MainActivity> mWeakReference;

        MyHandler(MainActivity activity) {
            mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Log.e("11111111111", "2222222222222");
            if (mWeakReference.get() != null) {

                mHandler.sendEmptyMessage(0);
            }
        }
    }

    private Handler mHandler = new MyHandler(this);

优点:能够解决内存泄漏。
缺点:由于采用弱引用,不清楚什么时候被回收。代码不可控。

(3)方式三:使用静态内部类

代码如下:

private static class MyHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Log.e("11111111111","2222222222222");
            mHandler.sendEmptyMessage(0);
        }
    }

private  static Handler mHandler = new MyHandler();

增加了static修饰符。静态内部类不持有外部类的引用。

优点:能够解决内存泄漏。
缺点:由于static修饰的对象,生命周期比较长。所以大量使用static容易导致内存溢出。

综上,还是要看自己的具体业务逻辑,采用不同的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值