关于 Handler 的内存泄露及处理方法
前言
今天在看程序员每天面试一题,看到了这样的一个面试题:关于 Handler 的内存泄露,及怎么样处理?
这是一个老生常谈的问题,网上也是有很多文章讲解。而且大部分的讲解都是大同小异,所以如果你已经了解过了那基本可以不用看本篇了。
写下来的主要目的是记录一下自己的了解、所得。
Part1
为什么会泄露?
要讲为什么会泄露,那就要从 Handler 的工作机制开始讲起
1.Android 的主线程在一开始启动的时候就已经默认的常见了一个 Looper 实例了,这个实例主要是用于处理一个一个的处理消息队列中的 Message,它的生命周期是跟整个应用的生命周期一样长的。
2.当我们使用 Handler 发送一个 Message 到Looper 处理的消息队列的时候,这个 Message就已经包含了改 handler 的实例了,这样 Looper在处理到该 Message 的时候才可以调用 handler.handlerMessage()。
3.在Java 的非静态内部类或匿名内部类会隐式持有外部类的实例。
so,在发送出去的 Message 还没有被处理之前,该 Message 持有的 Handler 就不会被回收,导致该Handler所在的外部类也不会回收。
Talk is cheep , show me the code! 无 code 言屌~
假设我们在 Activity 发送了这样的一个 Message:
在 Activi 创建的时候发送一个定时十分钟的消息,此时在十分钟还没有到的时候,我们按 Back 键,或者调用 finish结束当前的 Activity,该 Activity 其实并不会被回收,因为它的实例被 handler 隐式持有了,而 handler 又被 message 持有,此时 message 又安安静静的躺在 Looper 的消息队列中等待时间到被处理。所以这样子内存泄露了。
Part2
how to fix?
既然知道是了非静态内部类或者匿名内部类持有外部类而引起的,那么我们肯定就是从这方面入手了。
1.把 Handler 放在单独的类文件,或者使用静态内部类,这样避免持有外部类引用。
2.在静态内部类或是单独的类文件中需要用到外部类的时候,用弱引用来处理,在使用时再对该引用进行 null 判断
3.另外关于同样也需要将Runnable设置为静态的成员属性
那么照着这样的思路,修改之后的代码如下:
在经过修改之后,handler 就不会持有 activity 的应用,这样子 Activity 该被干掉还是会被干掉,改回收的东西照样会回收。
by the way:
在 handler中一定要重写 handleMessage()然后一定要进行非空判断。
不然定时到了,Looper 调用了 message 的 handler.handleMessage(),然后就会 nullPointException ,APP 就会 crash了~