分析:
在Android系統(tǒng)中,Handler是一個(gè)消息發(fā)送和處理機(jī)制的核心組件之一壹士,與之配套的其他主要組件還有Looper和Message磷雇,MessageQueue。
Message和Runnable類(lèi)是消息的載體躏救。MessageQueue是消息等待的隊(duì)列倦春。Looper則負(fù)責(zé)從隊(duì)列中取消息。
Handler有兩個(gè)主要作用:
1.安排調(diào)度(scheule)消息和可執(zhí)行的runnable落剪,可以立即執(zhí)行睁本,也可以安排在某個(gè)將來(lái)的時(shí)間點(diǎn)執(zhí)行。
2.讓某一個(gè)行為(action)在其他線程中執(zhí)行忠怖。
Handler是由系統(tǒng)所提供的一種異步消息處理的常用方式,一般情況下不會(huì)發(fā)生內(nèi)存泄露呢堰。
Handler為什么可能造成內(nèi)存泄漏。這里的內(nèi)存泄漏凡泣,常常指的是泄漏了Activity等組件枉疼。
public class ShanActivity extends Activity{
public Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
這有什么問(wèn)題呢。問(wèn)題在于該Handler的實(shí)例采用了內(nèi)部類(lèi)的寫(xiě)法鞋拟,它是ShanActivity這個(gè)實(shí)例的內(nèi)部類(lèi)骂维,在Java中,關(guān)于內(nèi)部類(lèi)有一個(gè)特點(diǎn):在java中贺纲,非靜態(tài)的內(nèi)部類(lèi)和匿名內(nèi)部類(lèi)都會(huì)隱式的持有一個(gè)外部類(lèi)的引用航闺。所以,該handler實(shí)例持有了ShanActivity的一個(gè)引用猴誊。
生命周期較長(zhǎng)的組件引用了生命周期較短的組件潦刃。Handler就是一種典型的示例,以上面的代碼舉例懈叹。ShanActivity可能會(huì)被泄漏乖杠,也就是該組件沒(méi)有用了,比如調(diào)用了finish()后澄成,垃圾回收器卻遲遲沒(méi)有回收該Activity胧洒。原因出在該實(shí)例的handler內(nèi)部類(lèi)引用了它,而該handler實(shí)例可能被MessageQueue引用著墨状。
問(wèn)題原因:
一般非靜態(tài)內(nèi)部類(lèi)持有外部類(lèi)的引用的情況下卫漫,造成外部類(lèi)在使用完成后不能被系統(tǒng)回收內(nèi)存,從而造成內(nèi)存泄漏歉胶。這里 Handler 持有外部類(lèi) Activity 的引用汛兜,而handler有又未處理完的message,一旦 Activity 被銷(xiāo)毀通今,而此時(shí) Handler 依然持有 Activity 引用粥谬,就會(huì)造成內(nèi)存泄漏肛根。
解決方法:
1.保證Activity被finish()時(shí)該線程的消息隊(duì)列沒(méi)有這個(gè)Activity的handler內(nèi)部類(lèi)的引用。這個(gè)場(chǎng)景是及其常見(jiàn)的漏策,因?yàn)閔andler經(jīng)常被用來(lái)發(fā)延時(shí)消息派哲。一個(gè)補(bǔ)救的辦法就是在該類(lèi)需要回收的時(shí)候,手動(dòng)地把消息隊(duì)列中的消息清空:mHandler.removeCallbacksAndMessages(null);
2.要么讓這個(gè)handler不持有Activity等外部組件實(shí)例掺喻,讓該Handler成為靜態(tài)內(nèi)部類(lèi)芭届。(靜態(tài)內(nèi)部類(lèi)是不持有外部類(lèi)的實(shí)例的,因而也就調(diào)用不了外部的實(shí)例方法了)
3.在2方法的基礎(chǔ)上感耙,為了能調(diào)用外部的實(shí)例方法褂乍,傳遞一個(gè)外部的弱引用進(jìn)來(lái))
4.將Handler放到抽取出來(lái)放入一個(gè)單獨(dú)的頂層類(lèi)文件中。
這里需要了解一下關(guān)于Java里面引用的知識(shí):
強(qiáng)引用(Strong Reference) | 默認(rèn)引用即硼。如果一個(gè)對(duì)象具有強(qiáng)引用逃片,垃圾回收器絕不會(huì)回收它。在內(nèi)存空 間不足時(shí)只酥,Java虛擬機(jī)寧愿拋出OutOfMemory的錯(cuò)誤褥实,使程序異常終止,也不會(huì)強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足問(wèn)題裂允。 |
---|---|
軟引用(SoftReference) | 如果內(nèi)存空間足夠损离,垃圾回收器就不會(huì)回收它,如果內(nèi)存空間不足了绝编,就會(huì)回收這些對(duì)象的內(nèi)存僻澎。 |
弱引用(WeakReference | 在垃圾回收器一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否瓮增,都會(huì)回收它的內(nèi)存怎棱。 |
虛引用(PhantomReference) | 如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣绷跑,在任何時(shí)候都可能被垃圾回收。 |
第三種凡资,需要一些額外的代碼砸捏,比較通用。
public class ShanActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<ShanActivity> mActivity;
public MyHandler(ShanActivity activity) {
mActivity = new WeakReference<ShanActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
ShanActivity activity = mActivity.get();
if (activity != null) {
//do Something
}
}
}
第四種方式隙赁,抽取做單獨(dú)封裝垦藏。
/**
* 實(shí)現(xiàn)回調(diào)弱引用的Handler
* 防止由于內(nèi)部持有導(dǎo)致的內(nèi)存泄露
* 傳入的Callback不能使用匿名實(shí)現(xiàn)的變量,必須與使用這個(gè)Handle的對(duì)象的生命周期一
* 致否則會(huì)被立即釋放掉了
*/
public class WeakRefHandler extends Handler {
private WeakReference<Callback> mWeakReference;
public WeakRefHandler(Callback callback) {
mWeakReference = new WeakReference<Handler.Callback>(callback);
}
public WeakRefHandler(Callback callback, Looper looper) {
super(looper);
mWeakReference = new WeakReference<Handler.Callback>(callback);
}
@Override
public void handleMessage(Message msg) {
if (mWeakReference != null && mWeakReference.get() != null) {
Callback callback = mWeakReference.get();
callback.handleMessage(msg);
}
}
}
由于是弱引用伞访,當(dāng)該類(lèi)需要被回收時(shí)掂骏,可以直接被回收掉。
WeakRefHandler的使用時(shí)如下:
private Handler.Callback mCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch(msg.what){
}
return true;
}
};
private Handler mHandler = new WeakRefHandler(mCallback);
總結(jié):
handler改為弱引用不是一概而論(大家只考慮在activity問(wèn)題厚掷,handler持有activity的引用)弟灼,解決問(wèn)題可以傳activity弱引用給handler就行级解,如果在service后臺(tái)用到handler,難道也弱引用田绑?合理使用handler勤哗,要明白為什么泄漏,不是所有場(chǎng)景都能用弱引用
tip:
removeCallbacksAndMessages: remove所有message和runnable
removeCallbacks: 只remove message掩驱,無(wú)法remove runnable