前言
Android的線程間通信主要Handler、Looper、Message、MessageQueue這四個。
1. Looper
在 Looper 中維持一個 Thread 對象以及 MessageQueue,通過 Looper 的構造函數(shù)可以知道:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper 在構造函數(shù)里干了兩件事:
- 將線程對象指向了創(chuàng)建 Looper 的線程
- 創(chuàng)建了一個新的 MessageQueue
Looper 主要有兩個方法:
- looper.loop()
- looper.prepare()
1.1 looper.loop()
在當前線程啟動一個Message loop機制,此段代碼將直接分析出Looper、Handler、Message、MessageQueue的關系:
public static void loop() {
final Looper me = myLooper();//獲得當前線程綁定的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//獲得與Looper綁定的MessageQueue
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//進入死循環(huán),不斷地去取對象,分發(fā)對象到Handler中消費
for (;;) {
Message msg = queue.next(); // 不斷的取下一個Message對象,在這里可能會造成堵塞。
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//在這里,開始分發(fā)Message了
//至于這個target是神馬?什么時候被賦值的?
//我們一會分析Handler的時候就會講到
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//當分發(fā)完Message之后,當然要標記將該Message標記為 *正在使用* 啦
msg.recycleUnchecked();
}
}
分析了上面的源代碼,我們可以意識到,最重要的方法是:
- queue.next()
- msg.target.dispatchMessage(msg)
- msg.recycleUnchecked()
Looper 中最重要的部分都是由 Message、MessageQueue 組成的!這段最重要的代碼中涉及到了四個對象,他們與彼此的關系如下:
- MessageQueue :裝食物的容器
- Message :被裝的食物
- msg.target :食物消費者
- Looper :負責分發(fā)食物的人
1.2 looper.prepare()
在當前線程關聯(lián)一個Looper對象。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//在當前線程綁定一個Looper
sThreadLocal.set(new Looper(quitAllowed));
}
以上代碼只做了兩件事:
判斷當前線程有沒有 Looper,如果有就拋出異常(Android 規(guī)定一個線程只能擁有一個與自己關聯(lián)的 Looper)。
如果沒有就設置一個新的 Looper 到當前線程
2. Handler
我們通常使用 Handler 的通常方法是:
Handler handler = new Handler(){
//這個方法是在哪里被回調(diào)的?
@Override
public void handleMessage(Message msg) {
//Handler your Message
}
};
還是先看一下 Handler 的構造方法:
//空參數(shù)的構造方法與之對應,這里只給出主要的代碼,具體大家可以到源碼中查看
public Handler(Callback callback, boolean async) {
//打印內(nèi)存泄露提醒log
....
//獲取與創(chuàng)建Handler線程綁定的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲取與Looper綁定的MessageQueue
//因為一個Looper就只有一個MessageQueue,也就是與當前線程綁定的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
帶上問題:
- Looper.loop() 死循環(huán)中的 msg.target 是什么時候被賦值的?
- handler.handlerMessage(msg) 在什么時候被回調(diào)的?
2.1 Looper.loop() 死循環(huán)中的msg.target是什么時候被賦值的?
要分析這個問題,很自然的我們想到從發(fā)送消息開始,無論是handler.sendMessage(msg) 還是 handler.sendEmptyMessage(what),我們最終都可以追溯到以下方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//引用Handler中的MessageQueue
//這個MessageQueue就是創(chuàng)建Looper時被創(chuàng)建的MessageQueue
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//將新來的Message加入到MessageQueue中
return enqueueMessage(queue, msg, uptimeMillis);
}
接下來分析 enqueueMessage(queue, msg, uptimeMillis):
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//顯而易見,在此給msg.target賦值handler!
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
2.2 handler.handleMessage(msg) 在什么時候被回調(diào)的?
通過以上的分析,我們很明確的知道 Message 中的 target 是在什么時候被賦值的了,我們先來分析在 Looper.loop() 中出現(xiàn)過的過的 dispatchMessage(msg) 方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//在這里回調(diào)
handleMessage(msg);
}
}
加上以上分析,我們將之前分析結(jié)果串起來,就可以知道了某些東西: Looper.loop() 不斷地獲取 MessageQueue 中的 Message,然后調(diào)用與 Message 綁定的 Handler對象的 dispatchMessage 方法,最后,我們看到了 handleMessage 就在 dispatchMessage 方法里被調(diào)用的。
3. Handler內(nèi)存泄漏分析及解決
首先,請瀏覽下面這段 handler 代碼:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
}
在使用 handler 時,這是一段很常見的代碼。但是,它卻會造成嚴重的內(nèi)存泄漏問題。在實際編寫中,我們往往會得到如下警告:
??:In Android, Handler classes should be static or leaks might occur.
3.1 分析
3.1.1 Android 角度
當 Android 應用程序啟動時,framework 會為該應用程序的主線程創(chuàng)建一個 Looper 對象。這個 Looper 對象包含一個簡單的消息隊列Message Queue,并且能夠循環(huán)的處理隊列中的消息。這些消息包括大多數(shù)應用程序 framework 事件,例如 Activity 生命周期方法調(diào)用、button 點擊等,這些消息都會被添加到消息隊列中并被逐個處理。
另外,主線程的 Looper 對象會伴隨該應用程序的整個生命周期。
然后,當主線程里,實例化一個 Handler 對象后,它就會自動與主線程 Looper 的消息隊列關聯(lián)起來。所有發(fā)送到消息隊列的消息 Message 都會擁有一個對 Handler 的引用,所以當 Looper 來處理消息時,會據(jù)此回調(diào) [Handler#handleMessage(Message)] 方法來處理消息。
3.1.2 Java角度
在 java 里,非靜態(tài)內(nèi)部類 和 匿名類 都會潛在的引用它們所屬的外部類。但是,靜態(tài)內(nèi)部類卻不會。
3.2 泄漏來源
請瀏覽下面一段代碼:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
當 activity 結(jié)束 (finish) 時,里面的延時消息在得到處理前,會一直保存在主線程的消息隊列里持續(xù)10分鐘。而且,由上文可知,這條消息持有對 handler 的引用,而 handler 又持有對其外部類(在這里,即 SampleActivity)的潛在引用。這條引用關系會一直保持直到消息得到處理,從而,這阻止了SampleActivity 被垃圾回收器回收,同時造成應用程序的泄漏。
??:上面代碼中的Runnable類--非靜態(tài)匿名類--同樣持有對其外部類的引用。從而也導致泄漏。
3.3 解決方案
首先,上面已經(jīng)明確了內(nèi)存泄漏來源:
只要有未處理的消息,那么消息會引用 handler,非靜態(tài)的 handler 又會引用外部類,即 Activity,導致 Activity 無法被回收,造成泄漏;
Runnable 類屬于非靜態(tài)匿名類,同樣會引用外部類。
為了解決遇到的問題,我們要明確一點:靜態(tài)內(nèi)部類不會持有對外部類的引用。所以,我們可以把 handler 類放在單獨的類文件中,或者使用靜態(tài)內(nèi)部類便可以避免泄漏。
另外,如果想要在 handler 內(nèi)部去調(diào)用所在的外部類 Activity,那么可以在 handler 內(nèi)部使用弱引用的方式指向所在 Activity,這樣統(tǒng)一不會導致內(nèi)存泄漏。
對于匿名類 Runnable,同樣可以將其設置為靜態(tài)類。因為靜態(tài)的匿名類不會持有對外部類的引用。
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
4. 總結(jié)
通過以上的分析,我們可以很清晰的知道 Handler、Looper、Message、MessageQueue 這四者的關系以及如何合作的了。
當我們調(diào)用 handler.sendMessage(msg) 方法發(fā)送一個 Message 時,實際上這個 Message 是發(fā)送到與當前線程綁定的一個 MessageQueue 中,然后與當前線程綁定的 Looper 將會不斷的從 MessageQueue 中取出新的 Message,調(diào)用 msg.target.dispathMessage(msg) 方法將消息分發(fā)到與 Message 綁定的 handler.handleMessage() 方法中。
一個 Thread 對應多個 Handler, 一個 Thread 對應一個 Looper 和 MessageQueue,Handler 與 Thread 共享 Looper 和 MessageQueue。 Message 只是消息的載體,將會被發(fā)送到與線程綁定的唯一的 MessageQueue 中,并且被與線程綁定的唯一的 Looper 分發(fā),被與其自身綁定的 Handler 消費。
申明:開始的圖片來源網(wǎng)絡,侵刪