1.使用靜態(tài)內(nèi)部類和弱引用
自定義靜態(tài)內(nèi)部類繼承Handler
因為靜態(tài)內(nèi)部類不能訪問外部類的非靜態(tài)方法,所以需要有外部類的弱引用
private HandlermUIHandler =new MyUIHandler(this);
public static class MyUIHandlerextends Handler {
public final static int REFRESH_UI =0;
? ? private WeakReferencemActivity;
? ? public MyUIHandler(SleepAndReadCode activity) {
mActivity =new WeakReference<>(activity);
? ? }
@Override
? ? public void handleMessage(Message msg) {
SleepAndReadCode theActivity =mActivity.get();
? ? ? ? if (theActivity ==null){
super.handleMessage(msg);
return;
? ? ? ? }
switch (msg.what) {
case REFRESH_UI:
theActivity.refreshUI();
break;
? ? ? ? }
}
}
private void refreshUI(){
? ? //UI操作
}
2.使用Handler.removeCallbacksAndMessages()方法,不過每個handler對象都要調(diào)用一次龄寞,在activity的onDestroy()的生命周期中使用Handler.removeCallbacksAndMessages()
接下來我將從源碼分析內(nèi)存泄漏產(chǎn)生的原因腿箩,有興趣的同學(xué)可以看看疾棵。
如果你還不知道Android消息隊列模型,建議可以先去了解一下。
為什么Handler會持有activity的引用刽锤?
因為Handler在activity中創(chuàng)建,也就是new出來時朦佩,非靜態(tài)的內(nèi)部類會持有外部類的引用并思。
那為什么Handler持有activity的引用會導(dǎo)致內(nèi)存泄漏喃?
因為Message持有了handler语稠。
來看看Message是怎么持有Handler的:
p.target會在handler發(fā)送Message的時候宋彼,一般我們使用的時候用的是sendEmptyMessage或者之類的方法,最終都會通過Handler的enqueueMessage把發(fā)送方的hanlder置于Message中仙畦,于是Message便有了handler的引用
為什么Message持有了handler就會導(dǎo)致內(nèi)存泄漏喃输涕?
眾所周知UI線程在創(chuàng)建時會默認(rèn)創(chuàng)建一個Looper和Message隊列,來看看Looper中的loop()方法的源碼:
發(fā)現(xiàn)也沒什么特殊的慨畸,無非就是一個循環(huán)莱坎,循環(huán)讀取消息隊列有無Message,用完之后調(diào)用msg.recycleUnchecked()寸士,進(jìn)入看看:
發(fā)現(xiàn)這里就是把已經(jīng)使用的Message釋放掉檐什,target置為null,就沒有handler的引用了弱卡,那為什么會發(fā)生內(nèi)存泄漏喃乃正,讓我們來試想一種情況:
你的Message是延時執(zhí)行的,但是用戶可不會管婶博,點了back鍵瓮具,你的Activity應(yīng)該要被GC銷毀的,但是Message持有的Handler又有Activity的強(qiáng)引用,所以無法被GC回收名党,這時內(nèi)存泄漏就發(fā)生了垢箕;所以內(nèi)存泄漏不是一定發(fā)生的。
搞清楚為什么會發(fā)生內(nèi)存泄漏兑巾,那就知道解決辦法了条获。
1.使用靜態(tài)內(nèi)部類:適用于不用更新界面等情況,因為靜態(tài)內(nèi)部類是無法使用外部類的非靜態(tài)方法和變量的
2.使用靜態(tài)內(nèi)部類+弱引用:適用于要更新界面等情況
3.在onDestroy生命周期調(diào)用removeCallbacksAndMessages()方法
/**
* Remove any pending posts of callbacks and sent messages whose
* <var>obj</var> is <var>token</var>. ?If <var>token</var> is null,
* all callbacks and messages will be removed.
*/
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
來看看mQueue.removeCallbacksAndMessages()方法的源碼:
之前傳入的handler不為空蒋歌,繼續(xù)執(zhí)行
p一般不為空帅掘,為空也就不會產(chǎn)生內(nèi)存泄漏了
p.target == h判斷當(dāng)前Message是否為對應(yīng)Handler的Message,這也是為什么要每個Handler對象都要調(diào)用一次
所以是當(dāng)前線程的話也為true堂油,接下來因為傳入的object為null修档,也為真,不用看p.obj==object的真假,所以可以進(jìn)入循環(huán)府框,然后清空了整個Message隊列吱窝,第二個循環(huán)不會進(jìn)入(p已經(jīng)到隊列尾,為null了)迫靖,內(nèi)存泄漏解決院峡。
那如果傳入不為空喃?
那就會進(jìn)第二個循環(huán)系宜,單獨回收對應(yīng)的Message照激。
至此,想必你對Handler內(nèi)存泄漏的原因及解決方法有了一個清楚的認(rèn)識盹牧。
手打不易俩垃,點個贊唄。