一. 导论
在学习Android基础知识的时候,我们会接触到一个基本的概念,那就是消息机制,之后便会接触到Handler,Looper,MessageQueue,Message之间的关系,接下来我们来解析这个流程
二. Handler的说明
Handler主要是接收子线程发送的数据,并且用此数据配合主线程更新UI,用来与主线程交互。如果此时需要一个耗时的操作,例如:联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示”强制关闭”. 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,但是当子线程中有涉及到操作UI的操作时,就会对主线程产生危险,也就是说,更新UI只能在主线程中更新,在子线程中操作是危险的. 这个时候,Handler就出现了来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递)Message对象(里面包含数据), 把这些消息放入主线程队列中,配合主线程进行更新UI。
Handler可以分发Message对象和Runnable对象, 每个Handler实例,都会绑定到创建它的线程中(一般是位于主线程), 也就是说Handler对象初始化后,就默认与对它初始化的进程的消息队列绑定,因此可以利用Handler所包含的消息队列,制定一些操作的顺序
三. Handler的工作原理
3.1 MessageQueue
这是一种数据结构,表示一个消息队列,存放消息的地方。每一个线程最多只可以拥有一个MessageQueue。通常使用一个Looper对象对该对象的MessageQueue进行管理。
主线程创建的时候,系统会创建一个默认的Looper对象,而Looper对象的创建,会自动创建一个MessageQueue,但是当创建其它线程的时候,系统并不会主动为其创建Looper对象,当我们需要时,通过调用prepare()方法来实现。
3.2 Message
消息对象,MessageQueue中存放的对象。一个MessageQueue包含多个Message对象。Message实例对象的获得,通常使用Message.obtain()方法或者Handler.obtainMesssage()方法来获取。
对于上面的Message.obtain()方法,都有多种重载的方法可供选择。它的创建并不一定是直接创建Message实例,而是先从Message Pool(消息池)中看有没有可用的Message实例,存在则直接取出并返回这个实例;如果Message Pool里面没有可用的Message实例,则用给定的参数创建一个Message对象。调用removeMessages()时,将Message从MessageQueue删除,同时放入到Message Pool中。
当Message和messageQueue的定义清楚之后,如何管理MessageQueue便是一个问题,在Android中Looper的出现就是为了帮助我们管理MessageQueue。
3.3 Looper
这是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper对象的创建是通过perpare()方法来实现的。同时每一个Looper对象和一个线程关联。通过调用Looper.myLooper()可以获得当前线程的Looper对象,创建一个Looper对象时,会同时创建一个MessageQueue对象。除了主线程有默认的Looper对象,其它线程是没有的,所以不能接受Message。如需接受,需要自己自定义一个Looper对象(通过Prepare()方法),这样线程就有了自己的Looper对象和MessageQueue数据结构。
Looper从MessageQueue中取出Message,然后交由Handler的handleMessage进行处理。处理完成之后,调用Message.recycle()方法将其放入Message Pool中。
3.4 Handler
消息的处理者,负责发送消息和处理消息。Handler负责把将要传递的消息封装成Message,通过调用Handler对象obtainMessage()方法来实现;将消息传递给Looper,这是通过Handler对象的sendMessage()来实现的;继而由Handler将Message放入MessageQueue中,当Looper对象看到MessageQueue中含有Message,就通知Handler进行处理,而handler对象接收到该消息时,调用该方法的handleMessage()来处理。
四. 源码分析
4.1 我们首先来看一下Looper的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
以上的方法中创建了一个新的MessageQueue,这也就解释了上面的创建Looper的同时会创建一个MessageQueue
4.2 之后便是分析prepare方法
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,在第5行,将一个Looper的实例放入了ThreadLocal,并且2-4行判断了sThreadLocal是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。
对于以上的代码,我们提出了一个问题:
1. 对于一个线程,如果拥有多个Handler,此时这个线程有几个消息队列和几个Looper?只有一个消息队列,一个Looper,在Looper代码里面可以看到,在prepare方法里面可知,Android规定一个线程只能有一个与自己关联的Looper,在Looper的构造方法定义了一个MessageQueue,故就只有一个Looper和一个MessageQueue
4.3 以下是loop方法
public static void loop() {
// sThreadLocal存储的Looper实例,就是prepare方法里面存储的
final Looper me = myLooper();
// 如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 拿到了looper实例中的mQueue(消息队列)
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 (;;) {
// 取出一条消息。如果存在消息但是时间未到,就会发生阻塞,
// next()方法是一个无线循环的方法,如果消息队列中没有消息,那么next()方法会一直阻塞,当有新消息到来时,next会将这条消息返回同时也将这条消息从链表中删除。
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
// 将消息交给msg的target的dispatchMessage()方法去处理。msg的target对象是什么?其实就是handler对象,之后我们会分析这个值是在什么地方进行设置的
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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.recycleUnchecked();
}
}
先看第一句
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
其余的在代码里面进行注释
对于以上的代码,我们提出了一个问题:
msg.target 是在什么地方赋值的?这个是在Handler里面进行设置的,我们接下来分析这个赋值的过程
4.4 接下来分析Handler方法
public Handler(Callback callback, boolean async) {
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());
}
}
// 通过Looper.myLooper()获取了当前线程保存的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;
}
4.5 现在分析常见的SendMessage(msg)方法
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) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 看到没,这里就是将当前的Handler赋值为msg.target
**msg.target = this;**
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
由上述的代码可知,msg.target是在handler发送消息的时候赋值的,明确该消息的处理的对象,并且之后将该Message插入消息队列,接下来分析消息队列的插入
4.6 queue.enqueueMessage(msg, uptimeMillis) 消息分析
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 没有消息,则新建一个链表的头,可以看出消息队列的实际实现是链表
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 插入新的消息,插入的顺序是通过msg.when 来确定插入的位置的
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
由上面可以看出消息的插入是通过链表来实现的,插入的位置是通过msg.when 来确定位置的。
分析到这,是不是还有什么东西没有分析,对了,你一定想到了,在定义Handler的时候,你要复写handleMessage 方法,但是此方法是在什么地方调用?
我们在looper的时候,通过msg.target.dispatchMessage(msg);这句话发送消息,我们可以看一下这个方法,由于msg.target 是handler,所以在Handler里面去寻找这个方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 看到没,就是在此处进行的调用,
handleMessage(msg);
}
}
至此,消息机制基本分析完毕,我们还有一个需要注意的
我们应该注意到,上面Message里面还有一个Callback的Runnable对象,由此引发了下面的内容
handler 不仅可以发送msg,还可以传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。
以下的部分说明了Handler的post方法的线程和UI线程有什么关系。
这里需要说明,有时候为了方便,我们会直接写如下代码:
mHandler.post(new Runnable() {
@Override
public void run() {
Log.e("TAG", Thread.currentThread().getName());
mTxt.setText("yoxi");
}
});
然后run方法中可以写更新UI的代码,其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message.
注:产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
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) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
最终和handler.sendMessage一样,调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue.
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchMessage方法:
public void dispatchMessage(Message msg) {
// 如果不为null,则执行callback回调,也就是我们的Runnable对象。
if (msg.callback != null) {
handleCallback(msg);
} else {
// 这儿需要继承Handler.Callback接口
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 这儿就是我们要找的调用处
handleMessage(msg);
}
}
三次匹配只是形式上的不同,具体的使用那一种的方式,需要我们自己去取舍
>
五. 总结
1. 当我们调用handler.sendMessage(msg)方法发送一个Message时,实际上是将该消息发送到与当前线程绑定的MessageQueue里面,之后与当前线程绑定的looper会不断的从MessageQueue里面取出Message,然后调用msg.target.dispatchMessage(msg)方法,到handler的handleMessage(msg)里面去处理该消息。
2. Handler不仅可以发送消息,还可以发送Runnable对象,但是要注意此时并没有新创建一个线程。
3. 对于产生一个Message对象,可以new ,也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
4. 一个Thread可以对应多个Handler,一个Thread对应一个Looper和一个MessageQueue,也就是在同一个线程内,Handler共享同一个Looper和MessageQueue
5. 主线程的Looper和MessageQueue是在ActivityThread的main()方法里面初始化的,所以我们在主线程编写代码的时候,不用写这些代码。
参考文章
- http://blog.sina.com.cn/s/blog_77c6324101016jp8.html android中handler用法总结
- http://tanghaibo001.blog.163.com/blog/static/9068612020111287218197/ Android 中Message,MessageQueue,Looper,Handler详解+实例
但是该网站的文章不好查看,看一篇转载的位置,比较适合观看 该位置为:http://www.open-open.com/lib/view/open1331276072249.html - http://blog.csdn.net/yuzhiboyi/article/details/7562245 Android之Handler详解(一)
- http://www.cnblogs.com/hnrainll/p/3597246.html Android HandlerThread 的使用及其Demo
- http://blog.csdn.net/lmj623565791/article/details/38377229 Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html android的消息处理机制(图+源码分析)—Looper,Handler,Message
- 最后这一篇是讲解具体的Java和native的部分
http://www.cnblogs.com/angeldevil/p/3340644.html Android消息处理机制(Handler、Looper、MessageQueue与Message)