Handler使用不当导致泄漏

场景

Fragment生命周期结束后没有及时remove message,导致Fragment无法释放

  1. handler.postDelay(runnable, 2000)

flowchart TB MessageQueue --> mMessages mMessages --> next next --> Message Message --> callback callback --匿名内部类反向持有--> Fragment
  1. 自定义Handler

Handler匿名内部类持有Fragment或者Activity

flowchart TB MessageQueue --> mMessages mMessages --> next next --> Message Message --> target target --> Handler Handler --匿名内部类反向持有--> Fragment

Message

classDiagram MessageQueue*--Message class MessageQueue{ +mMessages: Message +next():Message -nativePollOnce() } class Message{ +what: int +arg1: int +arg2: int +target: Handler +next: Message +callback: Runnable }

处理方式

解决办法是Fragment/Activity销毁时清空消息队列 mHandler.removeCallbacksAndMessages(null);

或者内部类使用WeakReference弱引用Fragment/Activity

引用链原理

当Activity/Fragment销毁时,Looper所在线程(GC Root)通过Looper.loop()开启了一个死循环,在不断从消息队列MessageQueue中取出消息,在Framgent/Activity中postDelay发送的消息被添加到消息队列, 入队时,Handler会被设置给Message.target字段,

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        // 引用发送消息的Handler
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        ....
        return queue.enqueueMessage(msg, uptimeMillis);
    }

postDelay函数会将Runnable设置到Message.callback字段

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

所以不管是post了一个内部类的Runnable还是,Handler本身是内部类,假设此Message作为延迟消息,将会一直存在消息队列中,由于GC root的引用从而导致Message持有的callback或者target指向的handler无法释放,其持有的Fragment也无法被回收,导致内存泄漏

Fragment引用链

春风花气馥,秋月寒江湛