对于Handler造成Activity内存泄漏的理解

这篇文章:Handler系列:Handler为什么会引起内存泄漏,怎么解决?,写的很仓促。重新梳理一下。

Handler为什么会持有Activity的引用?

匿名内部类或者成员内部类的方式,重写handleMessage()方法,内部类默认持有了外部类的引用。所以Handler对象持有Activity的对象。具体参考上一篇:采用匿名内部类形式定义Handler有什么不妥?

Activity执行onDestroy()后,Handler被谁引用了,为什么不能被释放?

整个引用链是这样的:Thead—>ThreadLocalMap—>Entry[]—>Looper—>MessageQueue—>Message—>Handler—>Activity

  • Thread(main线程不会结束,活跃的线程是GC ROOT对象)
  • ThreadLocalMap(每个Thread都有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals
public class Thread implements Runnable {
	...
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...
}
  • Entry[] (ThreadLocalMap中有个Entry数组类型的成员变量table)
public class ThreadLocal<T> {
    ...
    static class ThreadLocalMap {
        private Entry[] table;

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    }
}
  • Looper (Main线程的looper对象,与ThreadLocal一起创造了一Entry对象,保存在Entry数组里面。)
	//ActivityThread的main()方法中:
    Looper.prepareMainLooper();

	//Looper.java
	public static void prepareMainLooper() {
        prepare(false); //这里面创建了Looper对象
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed)); //Looper对象在这里创建
    }

	//ThreadLocal.java
    public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value); //这个this就是ThreadLocal本身
    }

  • MessageQueue(Looper对象里面持有了MessageQueue类型的成员变量mQueue
public final class Looper {
	...
	
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed); //构造器里面初始化
        mThread = Thread.currentThread();
    }
    ...

}   
  • Message(MessageQueue里面有一个Message单链表,这个大家就很熟悉了,不贴代码了。)
  • Handler(Message的成员变量target引用了Handler对象)
  • Activity (匿名内部类重写Handler的handleMessage()方法,这个匿名内部类默认持有外部类Activity的引用。)

感谢:
重新理解为什么 Handler 可能导致内存泄露?

Android中的GC Root对象有哪些?

简单讲:

  • 活跃的线程Thread
  • static变量

具体参考:
Android开发从GC root分析内存泄漏
https://www.yourkit.com/docs/java/help/gc_roots.jsp

解决ML的时候,静态内部类已经不持有Activity的引用了,为什么要使用弱引用?

静态内部类不持有Activity的引用了,同时也没法访问Activity中的成员变量了,比如TextView,ImageView。要想访问TextView,又不造成Activity无法被回收,就使用弱引用。

public class MemoryLeakActivity extends AppCompatActivity {
    private TextView txt;
    private MyHandler handler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_leak);

        txt = findViewById(R.id.txt);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //延迟的Message对象-->Handler对象-->当前Activity对象
                handler.sendEmptyMessageDelayed(0, 5*1000);
            }
        });
    }

    private static class MyHandler extends Handler{
        private WeakReference<MemoryLeakActivity> wr;

        public MyHandler(MemoryLeakActivity mla){
            wr = new WeakReference<>(mla);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Log.d("MemoryLeakActivity","延迟处理消息");
            MemoryLeakActivity mla = wr.get();
            if(mla!=null){
            	mla.txt.setText("xxxx");
            }
        }
    }
}

Handler内存泄漏详解及其解决方案

其他细节推荐阅读:
Handler内存泄露原理及解决方法
Java 强引用和弱引用及Handler内存泄漏问题
Handler 引起的内存泄露分析以及解决方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangjin1120

可靠的文章费时费力,希望支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值