Handler內(nèi)存泄漏的原因是什么牺氨?
這里提醒我們,這個(gè)handler必須時(shí)靜態(tài)的猴凹,否則有可能會(huì)產(chǎn)生內(nèi)存泄漏夷狰,所有的內(nèi)部類都會(huì)引用外部類的引用,為什么只有handler會(huì)提示內(nèi)存泄漏呢郊霎?
原因之一內(nèi)部類持有外部類的引用沼头,這都知道。
因?yàn)閮?nèi)部類持有外部類的引用书劝,所以這里可以直接調(diào)用到text1.setText("xxx");
要說清除handler內(nèi)存泄漏的原因进倍,這里就要提到JVM的垃圾回收機(jī)制中GCRoot的概念,有個(gè)可達(dá)性分析购对,標(biāo)記的過程就是基于可達(dá)性分析來進(jìn)行的帜矾,什么時(shí)可達(dá)性分析澄港?就是一個(gè)持有鏈步绸,GCRoot持有的就是可達(dá)的蔚约,或者時(shí)被GCRoot簡介持有的,都是可達(dá)的烙如,一旦可達(dá),意味這它就被這個(gè)GCRoot持有了毅否,被GCRoot持有的對(duì)象都是不能夠進(jìn)行垃圾回收的
public class Handler {
.......
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
......
}
public final class Message implements Parcelable {
........
@UnsupportedAppUsage
/*package*/ Handler target;
@UnsupportedAppUsage
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
@UnsupportedAppUsage
/*package*/ Message next;
......
首先匿名內(nèi)部類持有了外部類的鏈條亚铁,handler持有了Activity,handler->Activity,那handler被誰持有了螟加,看上面的代碼徘溢,在enqueueMessage的時(shí)候msg.target = this;再看Message的類吞琐,變量target,就是Handler然爆,所以這里鏈條就變成了Message->Handler->Activity站粟,那message被誰持有,我們的message入隊(duì)列的時(shí)候曾雕,丟給了messageQueue奴烙,所以messageQueue持有了message。messageQueue->Message->handler->Activity剖张。
public final class Looper {
.....
final MessageQueue mQueue
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
}
}
Looper的loop()操作切诀,會(huì)對(duì)MessageQueue進(jìn)行輪詢操作,所以是Looper持有了MessageQueue搔弄,Looper->messageQueue->Message->handler->Activity幅虑。然后就是ActivityThread持有了Looper,ActivityThread->Looper->messageQueue->Message->handler->Activity。
分析:
ActivityThread不會(huì)釋放Looper顾犹,Looper持有的MessageQueue倒庵,不會(huì)釋放,message呢炫刷?message會(huì)釋放嗎擎宝?
public static void loop() {
......
msg.target.dispatchMessage(msg);
......
msg.recycleUnchecked();
}
在message處理完的時(shí)候會(huì)被釋放,會(huì)調(diào)用msg.recycleUnchecked();去釋放柬唯。如果message做了一個(gè)定時(shí)器认臊,2分鐘之后才執(zhí)行,這個(gè)message不會(huì)被釋放锄奢,如果是20分鐘失晴,那就意味著這個(gè)message會(huì)在messageQueue里面至少20分鐘,這因?yàn)閙essage會(huì)持有handler拘央,持有activity涂屁,這個(gè)時(shí)候就是內(nèi)存泄漏,按道理來說灰伟,activity走onDestroy()拆又,就會(huì)立即釋放,但是由于message間接持有了activity栏账,所以它不會(huì)釋放帖族,handler導(dǎo)致activity內(nèi)存泄漏的原因就是因?yàn)檫@個(gè)持有鏈條的存在。
最簡單的解決辦法就是用static挡爵,handle創(chuàng)建的是靜態(tài)的竖般,就沒有問題,不持有外部對(duì)象茶鹃,它是全局的涣雕。