首先得说下为什么写这篇文章。因为学习了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分析,进一步推断,最后再用代码论证,个人觉得这种办法是比较实际的,而且更能学到东西。
转载于:https://blog.51cto.com/cthhqu/1283673