相信很多人都這樣用過Handler:
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
//更新UI
//...
super.handleMessage(msg);
}
};
new Thread(new Runnable() {
@Override
public void run() {
//耗時(shí)操作
//...
handler.sendEmptyMessage(0);
}
}).start();
很常見的一段段碼颖医,對沒錯检激,但是確實(shí)發(fā)生了內(nèi)存泄漏。假如當(dāng)在處理耗時(shí)操作的時(shí)候允蜈,可能是某段網(wǎng)絡(luò)請求磷箕,也有可能是很復(fù)雜的計(jì)算选酗,這時(shí)用戶等的不耐煩了退出當(dāng)前界面,由于異步操作岳枷,此時(shí)雖然界面銷毀了芒填,但是任務(wù)還在繼續(xù)呜叫,由于線程中,引用這當(dāng)前Activity中的對象handler殿衰,而handler對象又隱式的應(yīng)用了當(dāng)前的Activity朱庆,因此雖然界面銷毀了,但是Activity并不會被回收闷祥。當(dāng)任務(wù)執(zhí)行完后娱颊,調(diào)用了handler方法,方法中又調(diào)用了更新界面蜀踏,就會出現(xiàn)空指針異常崩潰维蒙。這算是最好的情況了,更嚴(yán)重的是由于Activity無法被銷毀果覆,那么就是占著那個地方不做事颅痊,這就是所謂的站著茅坑不拉屎。那么這塊資源便浪費(fèi)了局待,當(dāng)用戶來回反復(fù)切換的時(shí)候斑响,資源占用越來越大,最終就算不空指針異常 也會出現(xiàn)OOM,這是由于內(nèi)存泄漏導(dǎo)致的钳榨。
內(nèi)存泄露的危害
只有一個舰罚,那就是虛擬機(jī)占用內(nèi)存過高,導(dǎo)致OOM(內(nèi)存溢出)薛耻,程序出錯营罢。對于Android應(yīng)用來說,就是你的用戶打開一個Activity饼齿,使用完之后關(guān)閉它饲漾,內(nèi)存泄露;又打開缕溉,又關(guān)閉考传,又泄露;幾次之后证鸥,程序占用內(nèi)存超過系統(tǒng)限制僚楞。
解決方案
- 在關(guān)閉Activity的時(shí)候停掉你的后臺線程任務(wù)。線程停掉了枉层,就相當(dāng)于切斷了Handler和外部連接的線泉褐,Activity自然會在合適的時(shí)候被回收。
- 如果你的Handler是被delay的Message持有了引用鸟蜡,那么使用相應(yīng)的Handler的removeCallbacks()方法膜赃,把消息對象從消息隊(duì)列移除就行了。
- 我們換一種寫法矩欠,將handler對象聲明成靜態(tài)內(nèi)部類财剖,靜態(tài)類不持有外部類的對象,所以你的Activity可以隨意被回收癌淮。
private TestHandler testHandler=new TestHandler();
static class TestHandler extends Handler{
@Override
public void handleMessage(Message msg) {
//更新界面操作
//...
super.handleMessage(msg);
}
}
很快你會發(fā)現(xiàn)編譯無法通過躺坟。因?yàn)樯厦嬲f過了。靜態(tài)類是不持有外部類的對象的乳蓄,因此如果你在里面寫了更新界面的操作當(dāng)然會編譯無法通過咪橙。解決辦法當(dāng)然有,就在靜態(tài)類中在增加一個外部類的弱引用虚倒。
static class MyHandler extends Handler{
private WeakReference<SearchContantActivity> mContext;
public MyHandler(SearchContantActivity activity){
mContext=new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
if(msg.what==0){
SearchContantActivity activity=mContext.get();
if(activity!=null){
activity.xxx
}
}
super.handleMessage(msg);
}
}
但是這種仍然會出現(xiàn)異常美侦,當(dāng)你的界面退出后,界面的控件都會被銷毀了魂奥,此時(shí)異步任務(wù)執(zhí)行完,觸發(fā)了handler發(fā)送消息菠剩,這時(shí)handler仍然會受到,從弱引用中拿到的Activity還是存在的耻煤,這時(shí)候你在去更新界面具壮,那么理所當(dāng)然就發(fā)生了NullPointException.所以最好的辦法就是界面在正常發(fā)送handler,界面銷毀就不發(fā)送handler哈蝇,徹底切斷它與Activity的聯(lián)系棺妓,那么就一勞永逸了。