Andorid中Handler造成的内存泄漏浅析与处理

来源:互联网 发布:win7怎样设置网络连接 编辑:程序博客网 时间:2024/06/05 09:33

Andorid中Handler造成的内存泄漏浅析与处理

概述:

Handler机制是Android中提供的一个异步回调机制,我们可以在完成一个耗时任务后作出相应通知。

每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中 引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好 处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。但是,正是这个机制,当Activity被销毁时,消息队列中有信息尚未被处理,容易引起内存泄漏。

 

正文:

下面我们举个例子:

1. 新建一个工程, 配置好leakCanary内存泄漏检测环境。(leakCanary:第三方Android和Java的内存泄露检测库)

2. 布局好UI 。(界面布局仅一个button, activity_main.xml文件中加个Button即可,Button 的ID为send_btn).

3. 在MainActivity中添加如下代码:

 

在平常项目开发中,我们经常这样使用handler类,在一个界面中加载数据loadData,加载数据完成后,发送Message,然后在在Handler的handlerMessage()中去处理我们的消息。

运行代码,我们做如下操作:

1. 点击按钮,在20秒内点击手机返回键,关闭MainActivity;

2. 等待10秒左右,会出现以下结果,此时,LeakCanary提示内存泄漏:


 

分析:

由于mhandler是Handler的匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,那么当这个Activity退出时,消息队列中还有未处理的消息或者正在处理消息,将导致该Activity的内存资源无法及时回收,引发内存泄漏。

解决方法:

使用静态内部类,将上面的代码改一下:

 

 

修改代码后运行,重复上面相同的操作,LeakCanary并没有报出内存泄漏。但是在开发中,在我们平时使用handlerMessage处理消息时,很多时候需要去使用外部类MainActivity的函数,比如我们需要更新MainActivity的UI 。

Handler使用方式升级版: 使用弱引用 -解决静态内部类访问外部类

名词解释:弱引用-----可以被JAVA 虚拟机顺利垃圾回收的一种引用方式

代码流程如下:

1.  修改一下我们刚才例子中的UI :在MainActity的布局文件 activity_main.xml中添加一个TextView ,并且在MainActity的oncreate找到并初始化它。

代码和效果示意如下:

  

 

2. 我们在handlerMessage中,给TextView设置值,请注意红色方框内的弱引用使用方式:

 

分析上面的做法:

创建一个静态Handler内部类,然后对Handler持有的外部对象使用弱引用,这样在回收时也可以回收Handler持有的对象,解决了我们内存泄漏以及访问外部对象的问题。

但是,这样子还不够完美: 我们退出MainActivity后,Looper线程的消息队列中还是可能会有待处理的消息,啥意思呢?就是我们MainActivity退出后,消息队列里还有消息,即我们的例子中,20秒后,还收到消息队列中的消息。

更完美的做法:我们应该在Activity关闭的时候,移除消息队列中的消息。

 

 

总结:在我们以后开发中使用handler时,就可以使用静态类和弱引用,这样可以避免内存泄漏现象,达到内存优化的目的。

0 0