这篇文章: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的引用。)
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内存泄露原理及解决方法
Java 强引用和弱引用及Handler内存泄漏问题
Handler 引起的内存泄露分析以及解决方法