首先得說下爲什麼寫這篇文章。因爲學習了Handler,MessageQueue與Looper後,感覺三者的關係是越學越亂,有時看一下這個人寫的東西,感覺明白了,然後再看下另外一個人寫的,感覺又有點不一樣,大體是相同,但是總是會找出那麼一兩個矛盾點,也許是我個人的理解能力不行導致理解偏差吧,總之是我對不起那些辛苦寫博客的博主。畢竟學習光看別人的也沒用,還是得自己動手去驗證,更何況看別人的還看得那麼不解,所以我決定還是自己看API文檔和SDK的源碼研究下。
摘要:本文主要從讀API文檔開始,進行我對Handler、MessageQueue和Looper的推斷,得出推斷後我再跟蹤SDK各個類的源碼驗證我的判斷,進一步得到推論結果,最後利用代碼驗證我們關於三者關係的推論,同時介紹瞭如何使用Handler和Message。
2. 代碼實踐、驗證結論
========================================================================
Class Overview
A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
Class Overview
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare()
in the thread that is to run the loop, and then loop()
to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler
class.
Class Overview
Low-level class holding the list of messages to be dispatched by a Looper
. Messages are not added directly to a MessageQueue, but rather through MessageQueue.IdleHandler
objects associated with the Looper.
You can retrieve the MessageQueue for the current thread with Looper.myQueue()
.
可以看出Looper負責分配消息隊列中的消息。Message也不會直接加到消息隊列,而是通過MessageQueue.IdleHandler來與Looper互動。
推斷2:上面我們說Handler把消息發到消息隊列,再由Handler從消息隊列取下消息進行處理,而這裏說Looper是分配消息的,消息也不是直接加到消息隊列,而是通過MessageQueue.IdleHandler與Looper互動添加的。那麼到這裏可以推斷他們存在這樣一層關係——Handler封裝消息,把消息發給Looper,由Looper與MessageQueue進行交互,把消息添加到消息隊列,同樣由Looper把消息從消息隊列上取下,再交由Handler處理。
到底是不是如我們推斷那樣,下面通過讀SDK源碼來驗證推斷。
1.2 通過SDK源碼驗證推斷
首先我們必須先看下Handler源碼,看它是否真的發送消息是發給了Looper,首先得先看看Handler的構造函數:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /**
* Default constructor associates this handler with the queue for the
* current thread.
*
* If there isn't one, this handler won't be able to receive messages.
*/ /*無參構造函數*/
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0 ) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null ) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()" );
}
mQueue = mLooper.mQueue;
mCallback = null ;
} |
1 2 3 4 5 6 7 8 9 | /**
* Use the provided queue instead of the default one.
*/ /*有參構造函數*/
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null ;
} |
從兩個構造函數我們都可以看出想要構造一個Handler對象,都離不開Looper,無參構造函數是通過Looper.myLooper()來獲取Looper對象,並綁定Looper對象對應的MessageQueue。這一點驗證推斷1——在哪個線程實例化,該Handler綁定了哪個線程以及其消息隊列,綁定哪個線程,說白了就是綁定該線程的Looper。
那麼發送消息是怎麼發送的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0 );
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0 ) {
delayMillis = 0 ;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false ;
MessageQueue queue = mQueue;
if (queue != null ) {
msg.target = this ;
sent = queue.enqueueMessage(msg, uptimeMillis); //Handler發送消息實際是通過Looper獲得了消息隊列,使用消息隊列的enqueueMessage方法來發送
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue" );
Log.w( "Looper" , e.getMessage(), e);
}
return sent;
} |
從Handler的sendMessage方法一步一步跟蹤下去,發現其實到最後是調用先前通過mLooper.mQueue獲取到的消息隊列的enqueueMessage方法來添加消息。
那麼,是如何處理消息的呢?我們從API文檔裏面得知Looper的一個寫法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
} |
handleMessage是Handler如何處理消息的回調函數,我們可以通過重寫該函數實現我們自己定義的處理消息的方法。那它是怎麼被觸發的呢?Looper.loop()又是幹嘛的?
不妨看下loop函數的源碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | /**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/ /** *在線程中運行消息隊列 */
public static void loop() {
final Looper me = myLooper();
if (me == null ) { //判斷是否有Looper,沒有拋異常
throw new RuntimeException( "No Looper; Looper.prepare() wasn't called on this thread." );
}
final MessageQueue queue = me.mQueue;
// 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();
for (;;) { //循環處理消息隊列中的消息
Message msg = queue.next(); // might block
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);
}
msg.target.dispatchMessage(msg); //可以看到這邊調用了Handler的dispatchMessage
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);
}
msg.recycle();
}
} |
可以看到源碼比較關鍵的一步是調用Handler的dispatchMessage方法,而且是傳入了Message對象作爲參數,我們可以猜測該函數可能涉及到處理消息函數,因爲處理消息函數(handleMessage)也是在Handler中的。接下來看dispatchMessage的源碼。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null ) { //callback是一個Runnable變量,如果爲傳入則不執行
handleCallback(msg);
} else {
if (mCallback != null ) {
if (mCallback.handleMessage(msg)) {
return ;
}
}
handleMessage(msg); //果不其然,這邊調用了handleMessage
}
} |
由此到這裏,對於Handler如何建立如何發消息,如何處理消息,這個過程如何與MessageQueue和Looper互動我們比較清楚了,所以接下來做個總結。
1.3 三者關係總結
由此,到這裏可以得出我們的初步結論,想要利用Handler完成發送消息並處理消息的過程大概是這樣的(以線程A發消息給線程B爲例):
(1)實例化與線程A綁定的Handler(前提是該線程已經有Looper,沒有可通過prepare方法獲得):
兩種方法,一種是在線程A直接調用無參構造器實例化,使得Handler與線程A的Looper、MessageQueue綁定;
另一種是在其他線程使用Handler帶Looper參數的構造器,傳入線程A的Looper進行實例化,同樣可得與線程A的Looper、MessageQueue相綁定的Handler對象;
(2)重寫處理消息的函數handleMessage
(3)在線程B使用線程A的Handler對象發送一個消息,該Handler在實例化時已經通過Looper獲得了線程A的MessageQueue,Handler使用獲得的MessageQueue對象的enqueueMessage把消息添加到隊列以完成消息的發送;
(4)使用Looper的loop函數運行消息隊列,這個過程是loop把消息從消息隊列取下,傳給Handler的dispatchMessage,判斷該如何處理消息,如果沒有其他callback則調用到了Handler的handleMessage處理消息。
這裏可以驗證我們的推斷2,但是推斷2有一點是講錯了,handler並沒有把消息發給Looper,由Looper去處理,而是從Looper獲取了與MessageQueue的“話語權”,Handler通過使用MessageQueue的enqueueMessage方法進行消息的發送。而處理消息則是由Looper執行loop去循環MessageQueue,並調用Handler的dispatchMessage去處理消息。所以Handler是發送、處理消息的,Looper是管理MessageQueue與Handler通信的機制,而MessageQueue是負責保存、管理Message對象的。
三者的關係圖爲:
2. 代碼實踐、驗證結論
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | private static final Object sPoolSync = new Object();
private static Message sPool ;
private static int sPoolSize = 0 ;
private static final int MAX_POOL_SIZE = 50 ;
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized ( sPoolSync ) {
if ( sPool != null ) {
Message m = sPool ;
sPool = m. next ;
m. next = null ;
sPoolSize --;
return m;
}
}
return new Message(); } |
(1)同線程:
package cth.android.handlerexer; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener { private Button btn_sendMsg1; private Button btn_sendMsg2; private Button btn_sendMsg3; private Button btn_sendMsg4; private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { Log.i("cth","--arg1-->" + msg.arg1); Log.i("cth","--arg2-->" + msg.arg2); Log.i("cth","--what-->" + msg.what); Log.i("cth","--obj-->" + msg.obj); Log.i("cth","--getWhen-->" + msg.getWhen()); Log.i("cth","--getTarget-->" + msg.getTarget()); }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_sendMsg1 = (Button) findViewById(R.id.btn_sendMsg1); btn_sendMsg2 = (Button) findViewById(R.id.btn_sendMsg2); btn_sendMsg3 = (Button) findViewById(R.id.btn_sendMsg3); btn_sendMsg4 = (Button) findViewById(R.id.btn_sendMsg4); } @Override public void onClick(View v) { //各個函數的點擊事件 switch (v.getId()) { case R.id.btn_sendMsg1: Message msg = Message.obtain(); //通過obtain方法獲取一個消息對象 msg.arg1 = 1; //設定各種值 msg.arg2 = 2; msg.what = 3; msg.obj = "Message.obtain()"; handler.sendMessage(msg); //利用Handler把消息發送出去 //obtain的重載方法,直接設置message的值 Message msg1 = Message.obtain(handler, 3, 1, 2, "Message.Obtain(handler,what,arg1,arg2,obj)"); msg1.sendToTarget(); //原理還是利用Handler發送 break; case R.id.btn_sendMsg2: handler.sendEmptyMessage(3); //發送一個只帶what=3的空消息,雖說是空消息,但實際還是有利用Message.obtain()獲取。 handler.sendMessage(Message.obtain()); //發送空消息 break; case R.id.btn_sendMsg3: handler.sendEmptyMessageDelayed(4, 5000); //發送一個延時5秒的消息 break; case R.id.btn_sendMsg4: handler.sendEmptyMessageAtTime(3, 9000); //發送一個消息,在9秒內發送出去 break; } } }
(2)不同線程:
設立設立三個按鈕,一個是主線程向兩個子線程發送消息1、消息2。另外兩個按鍵是啓動兩個子線程,接收主線程利用子線程的Handler對象h1、h2發的消息,兩個子線程收到消息後,還利用主線程的Handler對象h3發回確認消息。 (注意:主線程發送消息前一定得先啓動兩個子線程)
package cth.android.handlerlooper; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener { private Button btn_sendMsg, btn_receiveMsg1, btn_receiveMsg2; private Handler h1 = null; private Handler h2 = null; private Handler h3 = new Handler(){ public void handleMessage(Message msg) { //主線程實例化h3,用以接受子線程發回的確認消息 Log.i("cth", "主線程接收消息中..."); super.handleMessage(msg); Log.i("cth", "--主線程收到的obj-->" + msg.obj); Log.i("cth","該消息隊列是" + Looper.myQueue().toString()); msg.recycle(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_sendMsg = (Button) findViewById(R.id.btn_sendMsg); btn_sendMsg.setOnClickListener(this); btn_receiveMsg1 = (Button) findViewById(R.id.btn_receiveMsg1); btn_receiveMsg1.setOnClickListener(this); btn_receiveMsg2 = (Button) findViewById(R.id.btn_receiveMsg2); btn_receiveMsg2.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_sendMsg: if (h1 != null) { Message msg = Message.obtain(h1, 1); msg.sendToTarget(); //發到子線程1的消息隊列 Log.i("cth", "已發送消息1"); } if (h2 != null) { Message msg = Message.obtain(h2, 2); msg.sendToTarget(); //發到子線程2的消息隊列 Log.i("cth", "已發送消息2"); } break; case R.id.btn_receiveMsg1: new Thread(new Runnable() { @Override public void run() { Log.i("cth", "新線程1開啓"); Looper.prepare(); h1 = new Handler() { public void handleMessage(Message msg) { //子線程1實例化h1 Log.i("cth", "子線程1接收消息中..."); super.handleMessage(msg); Log.i("cth", "--子線程1收到的what-->" + msg.what); Log.i("cth","該消息隊列是" + Looper.myQueue().toString()); Message msg3 = h3.obtainMessage(); msg3.obj = "子線程1收到what=" + msg.what; msg3.sendToTarget(); msg.recycle(); } }; Looper.loop(); } }).start(); break; case R.id.btn_receiveMsg2: new Thread(new Runnable() { @Override public void run() { Log.i("cth", "新線程2開啓"); Looper.prepare(); h2 = new Handler() { public void handleMessage(Message msg) { //子線程2實例化h2 Log.i("cth", "子線程2接收消息中..."); super.handleMessage(msg); Log.i("cth", "--子線程2收到的what-->" + msg.what); Log.i("cth","該消息隊列是" + Looper.myQueue().toString()); Message msg3 = h3.obtainMessage(); msg3.obj = "子線程2收到what=" + msg.what; msg3.sendToTarget(); msg.recycle(); } }; Looper.loop(); } }).start(); break; default: break; } } }
運行結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class myHandler extends Handler {
@Override
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
Log.i( "cth" , "Handler的sendMessageAtTime方法被調用" );
return super .sendMessageAtTime(msg, uptimeMillis);
}
@Override
public void dispatchMessage(Message msg) {
Log.i( "cth" , "Handler的dispatchMessage方法被調用" );
super .dispatchMessage(msg);
}
@Override
public void handleMessage(Message msg) {
Log.i( "cth" , "主線程收到消息" );
Log.i( "cth" , "Handler的handleMessage方法被調用" );
Log.i( "cth" , "--arg1-->" + msg.arg1);
Log.i( "cth" , "--arg2-->" + msg.arg2);
Log.i( "cth" , "--what-->" + msg.what);
Log.i( "cth" , "--obj-->" + msg.obj);
Log.i( "cth" , "--getWhen-->" + msg.getWhen());
Log.i( "cth" , "--getTarget-->" + msg.getTarget());
}
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | private Button btn_sendMsg;
private myHandler handler = new myHandler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_sendMsg = (Button) findViewById(R.id.btn_sendMsg);
btn_sendMsg.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
new Thread( new Runnable(){
@Override
public void run() {
Message msg = Message.obtain(); //通過obtain方法獲取一個消息對象
msg.arg1 = 1 ; //設定各種值
msg.arg2 = 2 ;
msg.what = 3 ;
msg.obj = "來自子線程的Message。" ;
Log.i( "cth" , "子線程發送消息" );
handler.sendMessage(msg); //利用Handler把消息發送出去
}
}).start();
}
});
} |
總之,看了API、源代碼後,自己再好好總結了一下,感覺思路清晰多了,以後遇到問題,建議還是不要急着百度看各種各樣的資料,先結合API文檔嘗試自己推斷一下,然後再看SDK分析,進一步推斷,最後再用代碼論證,個人覺得這種辦法是比較實際的,而且更能學到東西。