对于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 引起的内存泄露分析以及解决方法

猜你喜欢

转载自blog.csdn.net/zhangjin1120/article/details/131557348