Handler作为Activity的内部类所引起的内存泄露问题

来源:互联网 发布:2016淘宝618报名入口 编辑:程序博客网 时间:2024/04/26 05:10
        前天参加某公司的面试,被问到一个关于Handler作为Activity的内部类所引起的内存泄露的问题。虽然当时答上来了,但是还是想在这里记录一下。
        问题总共分为两问:
        1、Handler作为Activity的内部类来使用,会导致什么问题。
        第一问很容易答上来,非静态的内部类会持有外部类实例的引用。因此,当通过Handler.post(Runnable r)或者sendMessage(Message msg)一个任务到MessageQueue中去时,会因为Message持有Handler,而Handler持有Activity,从而形成Message-----Handler-----Activity的引用链。这样,当Avtivity的生命周期结束,而消息还没有来得及处理时,由于上述引用链的存在而导致Activity不能及时回收,从而会引起内存泄露。

        2、如果存在内存泄露,请说出它的完整引用链。
        当时,听到第二问的时候,一下子没有反应过来,自己不是已经在第一个问题的回答中阐述了吗?后来,和面试官确认了一下,要求是要说出导致内存泄露的完整引用链。于是,不得不将Thread、Looper、MessageQueue、Message、Handler之间的关系在脑子里迅速地过一遍。
        我们知道,对于一个Handler来说,必须要有一个Looper与之相关联,而Looper是要运行在线程中的。由于主线程的Looper在ActivityThreadhttp://androidxref.com/7.1.1_r6/xref/frameworks/base/core/java/android/app/ActivityThread.java 的main()方法中就已经创建了,因此在主线程中创建Hanlder,并且我们也希望经过该Handler所发送的消息在主线程得到处理时,那么就不必再显式地为其指定Looper了。否则,我们需要为其显式地去指定一个Looper。要得到一个带Looper的线程,我们可以自己去创建,也可以直接使用Android为我们提供的http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/java/android/os/HandlerThread.java。该类继承至Thread,并实现了run()方法, 在run()方法中实现了对成员变量mLooper的初始化,并通过Looper.loop()方法让线程进入一个死循环,以响应并处理投递到消息队列中的消息。既然已经有了现成的带Looper的线程,那么我们就可以建立一个工作在非主线程的Handler了:HandlerThread  workerThread = new HandlerThread("worker"); Handler woker = new Handler(workerThread.getLooper())。通过上面的分析,可以知道:所谓地Handler工作在哪个线程, 说的就是与Handler关联的Looper的loop()方法工作在哪个线程,也就是创建并持有Looper的那个线程。于是,Thread-----Looper之间存在引用链关系。
        我们知道,消息是通过Handler发送到消息队列中去的,现在Handler有了与之关联的Looper,但是却还没有接收消息的MessageQueue。但是,通过查看Looper类的源码 http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/java/android/os/Looper.java 可以知道其内部有一个叫做mQueue的MessageQueue类型的成员变量,这个变量的初始化时机是在Looper被构造的时候。因此,其实在将Looper与Handler相关联时,就已经附带了一个MessageQueue给Handler了。于是,Looper-----MessageQueue之间存在引用链。
        最后,结合上面的分析,就可以得到完整的引用链Thread----Looper---MessageQueue---Message---Handler---Activity。

 
1 0