零零碎碎的東西總是記不長久,僅僅學習別人的文章也只是他人咀嚼後留下的殘渣。無意中發現了這個每日一道面試題,想了想如果只是簡單地去思考,那麼不僅會收效甚微,甚至難一點的題目自己可能都懶得去想,堅持不下來。所以不如把每一次的思考、理解以及別人的見解記錄下來。不僅加深自己的理解,更要激勵自己堅持下去。
handler作用
SDK文件是這麼說的。
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
我們一般就是用來更新UI執行緒的。具體點就是在子執行緒進行耗時操作,比如獲取網路圖片,然後需要在主執行緒更新圖片,就需要handler+Message+Loop+MessageQueue來幫忙啦。
但是如果你直接建立一個handler物件,然後重寫內部handlerMessage方法,那麼AS一定會提醒你會有記憶體洩漏的可能。
為什麼會造成記憶體洩漏
Android記憶體洩漏:需要被GC回收的物件因為被其他存活的物件所持有引用,而導致GC不能回收此物件。那麼這塊記憶體就會在程式執行期間長期被佔據,造成系統記憶體的浪費,使系統執行緩慢甚至崩潰。
那麼handler什麼時候會造成記憶體洩漏呢?
傳送延遲訊息
眾所周知,匿名內部類持有外部類的引用,那麼handler物件就會持有activity物件的引用。handler傳送message到MessageQueue,message持有handler的引用,而MessageQueue會持有message的引用,而MessageQueue是屬於TLS(ThreadLocalStorage)執行緒,是與Activity不同的生命週期。
所以當Activity的生命週期結束後,而MessageQueue中還存在未處理的訊息,那麼上面一連串的引用關係就不允許Activity的物件被回收,就造成了記憶體洩漏。
解決辦法
知道了記憶體洩漏是由引用鏈造成的,那麼解決方法也就是破壞上面的引用鏈。
首先是引用的型別,有強引用、軟引用、弱引用、虛引用,上面的引用鏈都是強引用。
所以第一種方法,自定義靜態內部類,如果想使用外部類的方法,那就通過弱引用的方法引入Activity物件。
public class BaseActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void myHandleMessage(Message msg){}
static class MyHandler extends Handler{
WeakReference<BaseActivity> mActivityReference;
public MyHandler(BaseActivity activity){
mActivityReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
BaseActivity activity = mActivityReference.get();
if(activity != null){
activity.myHandleMessage(msg);
}
}
}
}
複製程式碼
你可以自定義在BaseActivity中,在其他Activity中建立Myahndler物件,通過重寫myHandleMessage方法進行訊息處理。
這種方法就是處理了Activity與Handler之間的引用,這種引用可以再GC時被回收。
第二種,就是處理後面的引用。既然是Activity要被回收時還有未被處理的訊息,那麼在Activity要被回收時清除訊息就可以了。
@Override
protected void onDestroy() {
super.onDestroy();
if(mHandler != null){
mHandler.removeCallbacksAndMessages(null);
}
}
複製程式碼