分析:
在Android系統(tǒng)中,Handler是一個消息發(fā)送和處理機制的核心組件之一,與之配套的其他主要組件還有Looper和Message,MessageQueue。
Message和Runnable類是消息的載體。MessageQueue是消息等待的隊列。Looper則負責從隊列中取消息。
Handler有兩個主要作用:
1.安排調度(scheule)消息和可執(zhí)行的runnable,可以立即執(zhí)行,也可以安排在某個將來的時間點執(zhí)行。
2.讓某一個行為(action)在其他線程中執(zhí)行。
Handler是由系統(tǒng)所提供的一種異步消息處理的常用方式,一般情況下不會發(fā)生內存泄露。
Handler為什么可能造成內存泄漏。這里的內存泄漏,常常指的是泄漏了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);
}
}
這有什么問題呢。問題在于該Handler的實例采用了內部類的寫法,它是ShanActivity這個實例的內部類,在Java中,關于內部類有一個特點:在java中,非靜態(tài)的內部類和匿名內部類都會隱式的持有一個外部類的引用。所以,該handler實例持有了ShanActivity的一個引用。
生命周期較長的組件引用了生命周期較短的組件。Handler就是一種典型的示例,以上面的代碼舉例。ShanActivity可能會被泄漏,也就是該組件沒有用了,比如調用了finish()后,垃圾回收器卻遲遲沒有回收該Activity。原因出在該實例的handler內部類引用了它,而該handler實例可能被MessageQueue引用著。
問題原因:
一般非靜態(tài)內部類持有外部類的引用的情況下,造成外部類在使用完成后不能被系統(tǒng)回收內存,從而造成內存泄漏。這里 Handler 持有外部類 Activity 的引用,而handler有又未處理完的message,一旦 Activity 被銷毀,而此時 Handler 依然持有 Activity 引用,就會造成內存泄漏。
解決方法:
1.保證Activity被finish()時該線程的消息隊列沒有這個Activity的handler內部類的引用。這個場景是及其常見的,因為handler經常被用來發(fā)延時消息。一個補救的辦法就是在該類需要回收的時候,手動地把消息隊列中的消息清空:mHandler.removeCallbacksAndMessages(null);
2.要么讓這個handler不持有Activity等外部組件實例,讓該Handler成為靜態(tài)內部類。(靜態(tài)內部類是不持有外部類的實例的,因而也就調用不了外部的實例方法了)
3.在2方法的基礎上,為了能調用外部的實例方法,傳遞一個外部的弱引用進來)
4.將Handler放到抽取出來放入一個單獨的頂層類文件中。
這里需要了解一下關于Java里面引用的知識:
| 強引用(Strong Reference) | 默認引用。如果一個對象具有強引用,垃圾回收器絕不會回收它。在內存空 間不足時,Java虛擬機寧愿拋出OutOfMemory的錯誤,使程序異常終止,也不會強引用的對象來解決內存不足問題。 |
|---|---|
| 軟引用(SoftReference) | 如果內存空間足夠,垃圾回收器就不會回收它,如果內存空間不足了,就會回收這些對象的內存。 |
| 弱引用(WeakReference | 在垃圾回收器一旦發(fā)現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。 |
| 虛引用(PhantomReference) | 如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。 |
第三種,需要一些額外的代碼,比較通用。
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
}
}
}
第四種方式,抽取做單獨封裝。
/**
* 實現回調弱引用的Handler
* 防止由于內部持有導致的內存泄露
* 傳入的Callback不能使用匿名實現的變量,必須與使用這個Handle的對象的生命周期一
* 致否則會被立即釋放掉了
*/
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);
}
}
}
由于是弱引用,當該類需要被回收時,可以直接被回收掉。
WeakRefHandler的使用時如下:
private Handler.Callback mCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch(msg.what){
}
return true;
}
};
private Handler mHandler = new WeakRefHandler(mCallback);
總結:
handler改為弱引用不是一概而論(大家只考慮在activity問題,handler持有activity的引用),解決問題可以傳activity弱引用給handler就行,如果在service后臺用到handler,難道也弱引用?合理使用handler,要明白為什么泄漏,不是所有場景都能用弱引用
tip:
removeCallbacksAndMessages: remove所有message和runnable
removeCallbacks: 只remove message,無法remove runnable