在Android系統中,Handler是一個消息發送和處理機制的核心組件之一,與之配套的其他主要組件還有Looper和Message,MessageQueue。
根據官網的描述
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Handler有兩個主要作用:
1.安排調度(scheule)消息和可執行的runnable,可以立即執行,也可以安排在某個將來的時間點執行。
2.讓某一個行為(action)在其他線程中執行。
上面翻譯的意思也就是Handler主要作為一種消息收發的機制。
這個消息可以是單純的基本類型,也可以是某個類,或者一個可執行的行為(runnable)。Message和Runnable類是消息的載體。MessageQueue是消息等待的隊列。Looper則負責從隊列中取消息。
在官網描述中,有一段描述很重要:Each Handler instance is associated with a single thread and that thread's message queue。意思是一個Handler的實例和單個的線程和這個線程的MessageQueue相關聯。這得出了一個結論:如果這個MessageQueue中的消息是有某個Handler的instance(實例)的引用的。
關於這一點,其實不難理解:Looper處理消息Message類的時候,需要調用Handler的handleMessage吧,這就需要知道是哪個Handler的實例,才能調用Handler.handleMessge()
現在回到題目的問題上。Handler為什么可能造成內存泄漏。這里的內存泄漏,常常指的是泄漏了Activity等組件。可能引起泄漏的操作是這種格式的代碼:
public class TestActivity 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); } }
注意這是上面的代碼是有問題的代碼,但不一定會引起內存泄漏,只是有可能,泄漏對象的是SampleActivity實例。
這有什么問題呢。問題在於該Handler的實例采用了內部類的寫法,它是SampleActivity這個實例的內部類,在Java中,關於內部類有一個特點:在java中,非靜態的內部類和匿名內部類都會隱式的持有一個外部類的引用。所以,該handler實例持有了SampleActivity的一個引用。到這里,是不是有點頭緒了呢。
關於內存泄漏,在android中一個通用的說法是:生命周期較短的組件引用了生命周期較長的組件。Handler就是一種典型的示例,以上面的代碼舉例。SampleActivity可能會被泄漏,也就是該組件沒有用了,比如調用了finish()后,垃圾回收器卻遲遲沒有回收該Activity。原因出在該實例的handler內部類引用了它,而該handler實例可能被MessageQueue引用着。比如發送了一個延時消息到隊列中,那么就可能在隊列中存在很長時間,而消息隊列(MessageQueue)的生命周期等於它所在的線程。當大到Activity被finish()了后還在隊列中時,就滿足了上面的短生命周期引用長生命周期的條件。根據Java GC的規則,SampleActivity的引用計數不為0,故不會回收,回收的時機在handler發送的消息出隊列時。
從上面的說法中,可以思考得到相應的解決方法:
1.保證Activity被finish()時該線程的消息隊列沒有這個Activity的handler內部類的引用。
2.要么讓這個handler不持有Activity等外部組件實例,讓該Handler成為靜態內部類。(靜態內部類是不持有外部類的實例的,因而也就調用不了外部的實例方法了)
3.在2方法的基礎上,為了能調用外部的實例方法,傳遞一個外部的弱引用進來)
4.將Handler放到一個單獨的頂層類文件中。
最好的方法是哪一種呢?其實前三種方法都差不多,第四種如果是一些輕量的操作就太多余了。不過要說通用性,第三種是最為通用的。
如果用第一種,其具體的解決方法是當組件銷毀時,在恰當的時機調用handler的removeCallbacksAndMessages(null),如果是在Activity中,則是在onDestroy()的生命周期回調中調用。如果是Activity等具有明確生命周期的組件時可以這么做,但要是在自定義的類中,比如一個單例中,往往不能找好釋放的時機。而且開發人員有時會忘記調用remove消息的方法。
如果用第二種,當在handler內部需要調用外部類的非靜態方法時就達不到要求了。因為在Java中,靜態的內部類中不能調用外部非靜態的方法。
第三種,需要一些額外的代碼,但方法最為通用。
public class TestActivity extends Activity { private static class MyHandler extends Handler { private final WeakReference<TestActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<TestActivity>(activity); } @Override public void handleMessage(Message msg) { TestActivity activity = mActivity.get(); if (activity != null) { //do Something } } }
采用哪種方法其實都是可以的,具體看實際情況。
參考資料:
https://developer.android.com/reference/android/os/Handler 官方Reference。
https://blog.csdn.net/lqw_student/article/details/52954837