在常见的Android编程中,Handler经常被用在执行异步操作和处理返回结果的时候。通常我们的代码会这样实现。publicclassSampleActivityextendsActivity{privatefinalHandlermLeakyHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){//...}}}但是,实际上上面的代码可能会造成内存泄漏。当你使用Androidlint工具时,你会得到这样的警告在Android中,Handler类应该是静态的,否则可能会发生泄漏,在应用程序线程的MessageQueue上排队的消息也保留其目标Handler。如果Handler是一个内部类,它的外部类也会被保留。为了避免泄漏外部类,将Handler声明为静态嵌套类,对其外部类有一个WeakReference看到这里,可能还是有些迷惑。代码中哪些地方可能会导致内存泄漏,又是如何导致内存泄漏的呢?那我们就慢慢分析吧。1、Android应用启动时,会自动为应用的主线程创建一个Looper实例。Looper的主要工作是对消息队列中的消息对象进行逐个处理。在Android中,Android框架的所有事件(如Activity生命周期方法调用和按钮点击等)都被放入消息中,然后加入到消息队列中由Looper处理,Looper负责处理一个一个。Looper在主线程中的生命周期与当前应用一样长。2、在主线程中初始化一个Handler时,当我们向Looper处理的消息队列发送一个目标为这个Handler的消息时,实际发送的消息中已经包含了一个Handler实例的引用,只有这样Looper在处理这条消息时,可以调用Handler#handleMessage(Message)完成消息的正确处理。3.在Java中,非静态内部类和匿名内部类都隐含地持有对其外部类的引用。静态内部类不包含对外部类的引用。对于这个内容,可以查看Java的详细介绍:上面代码示例中“无效”的private修饰符确实有点难以检测内存泄漏,那么下面的例子就很明显了(Messagemsg){//...}}@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);//Postamessageanddelayitsexecutionfor10minutes.mLeakyHandler.postDelayed(newRunnable(){@Overridepublicvoidrun(){/*...*/}},1000*60*10);//GobacktothepreviousActivity.finish();}}分析上面的代码,当我们执行Activity的finish方法时,延迟的消息会在主线程消息队列中存在10分钟才被处理,而这个消息中还包含了对Handler的引用,而Handler是一个匿名内部类的实例,在外部保存着对SampleActivity的引用,所以这就导致SampleActivity无法回收,并且SampleActivity持有的很多资源是不能回收的。这就是我们常说的内存泄漏。请注意,上面的新Runnable也是由一个匿名内部类实现的,它也将持有对SampleActivity的引用并防止SampleActivity被回收。解决这个问题的思路是非静态内部类不适用。继承Handler时,要么放在单独的class文件中,要么使用静态内部类。因为静态内部类不持有外部类的引用,所以不会造成外部类实例的内存泄漏。当需要在静态内部类中调用外部Activity时,我们可以使用弱引用来处理。此外,还需要将Runnable设置为静态成员属性。注意:匿名内部类的静态实例不包含对外部类的引用。修改后不会导致内存泄露的代码如下publicclassSampleActivityextendsActivity{/***Instancesofstaticinnerclassesdonotholdanimplicit*referencetotheirouterclass.*/privatestaticclassMyHandlerextendsHandler{privatefinalWeakReference
