首先得说下为什么写这篇文章。因为学习了Handler,MessageQueue与Looper后,感觉三者的关系是越学越乱,有时看一下这个人写的东西,感觉明白了,然后再看下另外一个人写的,感觉又有点不一样,大体是相同,但是总是会找出那么一两个矛盾点,也许是我个人的理解能力不行导致理解偏差吧,总之是我对不起那些辛苦写博客的博主。毕竟学习光看别人的也没用,还是得自己动手去验证,更何况看别人的还看得那么不解,所以我决定还是自己看API文档和SDK的源码研究下。
摘要:本文主要从读API文档开始,进行我对Handler、MessageQueue和Looper的推断,得出推断后我再跟踪SDK各个类的源码验证我的判断,进一步得到推论结果,最后利用代码验证我们关于三者关系的推论,同时介绍了如何使用Handler和Message。
2. 代码实践、验证结论
========================================================================
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 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.
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分析,进一步推断,最后再用代码论证,个人觉得这种办法是比较实际的,而且更能学到东西。