Handler消息机制(Java层)
1.简介
Android应用程序是通过消息来驱动的,系统为每一个应用程序维护了一个消息队列,应用程序的主线程不断地从这个消息队列中获取消息,然后就对这些消息进行处理,这样就实现了通过消息来驱动应用程序的执行。
通过消息驱动的好处是,消息的发送方只要把消息放在应用程序的消息队列中就行了,而不需要等待消息的接收方处理完整个消息才返回,这样就可以提高系统的并发性。
各个元素的关系:
- Runnable和Message可以被压入某个MessageQueue中,形成一个集合。
- Looper循环地去做某件事情。
- Handler是“处理事情的地方”。
一句话概括他们是:
Looper不断获取MessageQueue中的一个Message,然后由Handler来处理。
就像中央处理器(Looper)不断从内存(MessageQueue)中读取指令(Message),执行指令(Handler),最终产生结果。
Looper负责创建一个MessageQueue,然后进入无限循环,不断从该MessageQueue中获取一个Message,将Message分发给对应的Handler处理。
Looper的主要作用:
- 与当前线程绑定,保证任何一个线程只有一个Looper实例,同时Looper也只有一个MessageQueue。
- Looper会不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。
与Windows应用程序的消息处理过程一样,Android应用程序的消息处理机制也是由消息循环、消息发送和消息处理这三部分组成。接下来将从源码的角度来分析这三个过程。
2.消息循环
在消息处理机制中,消息都是存放在一个消息队列中,而应用程序的主线程就是围绕这个消息队列进入一个无限循环的,直到应用程序退出。如果消息队列中有消息,应用程序的主线程就会把它取出来,并分发给相应的Handler进行处理;如果队列中没有消息,应用程序的主线程就会进入空闲等待状态,等待下一个消息的到来。消息的循环过程是有Looper类实现的,我们先看一下一个典型的Handler的用法:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();//Looper的准备工作
mHandler = new Handler() {//创建处理消息的Handler
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();//消息循环
}
}
2.1 Looper.prepare()
首先从Looper.prepare()方法开始:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//ThreadLocal变量是每个线程独有的,可以用来判断是否已经创建过Looper了,保证每个线程只有一个Looper对象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//将新创建的Looper对象保存在ThreadLocal变量中
sThreadLocal.set(new Looper(quitAllowed));
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建一个消息队列,按照先进先出的原则进出
mThread = Thread.currentThread();//将当前线程保存在mThread变量中
}
可以看到,在Looper.prepare()方法中,创建了一个Looper对象,并将该Looper对象与当前线程进行关联了,保证一个线程只有一个Looper对象。同时在Looper中,创建了一个MessageQueue。这样的话,当前线程有一个Looper对象,Looper对象中又有一个MessageQueue。关系如下:currentThread->Looper->messageQueue。
MessageQueue的构造方法如下:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();//调用本地方法创建一个NativeMessageQueue
}
MessageQueue的初始化工作交由JNI方法nativeInit来实现了,它的主要工作是创建一个NativeMessageQueue,并把它保存在mPtr变量中。nativeInit方法的实现见Handler消息机制(Native)文章。
2.2 Handler的构建
接下来看Handler的创建:
//默认构造方法
public Handler() {
this(null, false);
}
/*
* Handler默认是同步的,除非async值为true。
* 异步消息代表中断或者事件,不需要与同步消息一起排队。
* 如果async为true,则handler会给每个Message或Runnable调用setAsynchronous(boolean)方法。
*/
public Handler(Callback callback, boolean async) {
....
//获取Looper对象,该Looper对象是当前线程关联的Looper对象,如果是在主线程,则是主线程的Looper对象
mLooper = Looper.myLooper();
//如果Looper对象为空,则抛出异常,因为必须先创建完Looper之后,才能创建Handler
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//获取Looper对象关联的消息队列
mCallback = callback;//如果有CallBack,则将CallBack赋值
mAsynchronous = async;
}
public static @Nullable Looper myLooper() {
//返回当前线程的Looper对象
return sThreadLocal.get();
}
还有一种Handler的构造方法:
public Handler(Looper looper) {
this(looper, null, false);
}
//直接使用参数中的Looper对象
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从Handler的构造方法中,可以看到,Hanlder主要是为了与Looper对象进行关联,即可以与当前线程的Looper对象关联,也可以与参数中的Looper关联。关联Looper之后,再与Looper的MessageQueue关联。这样的话,Handler就与某个Looper的MessageQueue进行关联了,Handler发送的消息将放入关联的MessageQueue中。
2.3 Looper.loop()
接下来看下Looper.loop()方法
public static void loop() {
//获取当前线程的Looper对象
final Looper me = myLooper();
//如果Looper为空,则抛出异常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取Looper关联的MessageQueue
final MessageQueue queue = me.mQueue;
.......
//死循环,不断从消息队列中取出消息,然后交给对应的Handler处理。
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
//可以用来打印Message开始执行前的信息
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
....
try {
//将消息分发给目标target处理,即交给对应的Handler处理。
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//可以用来打印Message消息执行后的信息
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
...
//回收消息
msg.recycleUnchecked();
}
}
这里就是进入消息循环中去了,它不断地从消息队列MessageQueue中去获取下一个要处理的消息msg,如果消息为空,则表示要退出循环了,否则的话就要调用这个消息的target对象的dispatchMessage函数来处理这个消息。
2.4 MessageQueue.next()
在从消息队列中获取消息时,可能会被阻塞,即MessageQueue的next函数会阻塞,它的实现如下:
Message next() {
final long ptr = mPtr;//获取保存的NativeMessageQueue
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;// 第一次遍历时,等待时间为0,表示需要立即返回
//循环遍历消息队列
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);// 调用本地方法,阻塞等待
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//寻找异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//如果当前时间小于消息执行的时间,说明消息还没有准备好,设置一个超时时间来唤醒下一次查询
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//获取一个消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();//标记消息正在使用
return msg;//返回消息队列首部的消息
}
} else {
nextPollTimeoutMillis = -1;//没有消息,则阻塞等待
}
//终止,销毁消息队列
if (mQuitting) {
dispose();
return null;
}
// 在进入等待状态前,先查看有没有IdleHandler需要执行
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 如果有需要执行的IdleHandler,则执行其queueIdle方法
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// queueIdle方法返回false,则移除该IdleHandler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
pendingIdleHandlerCount = 0;
// 执行完IdleHandler之后,需要将nextPollTimeoutMillis置为0,因为有可能在执行IdleHanlder的时候,已经有新的消息加入了消息队列,因此,需要重置nextPollTimeoutMillis的值。
nextPollTimeoutMillis = 0;
}
}
调用next函数时,有可能会让线程进入等待状态。有两种情况会让线程进入等待状态:一是当消息队列中没有消息时,它会使线程进入等待状态;二是消息队列中有消息,但是消息指定的执行时间还没有到这个时间,线程也会进入等待状态。消息队列中的消息是按时间先后来排序的。
nativePollOnce方法是一个JNI方法,表示需要阻塞等待的时间,nextPollTimeoutMillis表示需要等待的时间,为0时表示不等待,为-1时表示一直等待。
当nativePollOnce方法返回后,就去消息队列中查看是否有没有消息,如果消息队列中有消息,并且当前时间大于消息的执行时间,那么直接返回处理该消息。否则的话,就要继续等待到消息的执行时间。
在进入空闲等待状态前,如果应用程序注册了IdleHandler接口来处理一些事件,那么就会先执行这里的IdleHandler,然后再进入等待状态。MessageQueue提供了addIdleHandler和removeIdleHandler两个方法来注册和删除IdleHandler。
可以看到,从MessageQueue中获取Message的过程是,比较当前时间与消息执行的时间,如果当前时间小于消息执行时间,说明还未到消息执行的时机,设置一个超时时间,下次唤醒来执行消息。如果当前时间大于消息执行时间,则直接返回消息队列首部的消息。
3.消息的发送
应用程序的主线程准备好消息队列并且进入到消息循环后,就可以通过Hanlder往这个消息队列中发送消息了。
发送消息的方法有多个,这里我们以sendMessage方法为例来说明。
3.1 Handler.sendMessage()
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);
}
不管以何种方式发送消息,最终都会调用到sendMessageAtTime方法。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//获取当前Handler关联的MessageQueue。
//如果队列为空,则抛出异常
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//将消息入MessageQueue队列
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//将当前Hanlder保存到Message中的target,这样Message与Handler关联了。
//是否是异步的,在构造Handler的时候,初始化该变量值
if (mAsynchronous) {
//将消息类型设置为异步的
msg.setAsynchronous(true);
}
//将消息存入MessageQueue中
return queue.enqueueMessage(msg, uptimeMillis);
}
发送消息的过程其实就是将消息放入消息队列的过程,在将消息放入消息队列前,会把Handler关联到当前Message,这样当分发消息时,就可以知道处理该消息的Handler。
3.2 MessageQueue.enqueueMessage()
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;
//1.消息队列为空,或者消息执行时间为0或者小于队列首部消息执行时间,则将该消息放入消息队列首部
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 {
//2. 将Message插入到队列的中间某个位置,根据时间执行先后排序
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
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;
}
// 需要立即唤醒
if (needWake) {
nativeWake(mPtr);// 调用native方法唤醒
}
}
return true;
}
把消息加入到消息队列时,分两种情况,一种是当前消息队列为空时,这时候应用程序的主线程一般就是处于空闲等待状态,这时候需要唤醒它,此时只要把消息放在消息队列头部就可以了。另外一种情况是应用程序的消息队列不为空,这时候就不需要唤醒应用程序的主线程了,此时按消息的处理时间先后顺序,在消息队列中找到一个合适的位置插入。
当把消息加入消息队列后,如果应用程序的主线程正处于空闲等待状态,则需要调用nativeWake函数来唤醒它了,这是一个JNI方法,主要作用是唤醒应用程序的主线程。
4.消息的处理
在前面的消息循环过程中,当Looper.loop()方法从消息队列中获取到一个消息后,就会调用它的target对象的dispatchMessage函数来处理这个消息。这个target对象是在消息入队列时设置的,具体是在调用Handler的enqueueMessage()方法时设置的。因此消息的处理是调用Handler的dispatchMessage()方法,定义如下:
4.1 Handler.dispatchMessage()
public void dispatchMessage(Message msg) {
//1.如果Message的CallBack不为空,则直接调用handleCallBack方法
if (msg.callback != null) {
handleCallback(msg);
} else {
//2.如果Handler中的CallBack不为空,则调用CallBack的handleMessage()方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//3.如果前面的条件都不满足,则调用handleMessage方法
handleMessage(msg);
}
}
//直接调用message的CallBack的run方法
private static void handleCallback(Message message) {
message.callback.run();
}
//Handler中的callback接口
public interface Callback {
public boolean handleMessage(Message msg);
}
// Handler中的handleMessage()方法,由子类来实现
public void handleMessage(Message msg) {
}
可以看到,消息的处理的先后顺序是:
Message的CallBack > Handler的CallBack > Handler的handleMessage()方法。
5.ActivityThread的Looper与Handler
我们可以看下主线程的消息处理是怎样的一个过程,在ActivityThread的main()方法中,循环处理消息:
public static void main(String[] args) {
//初始化执行环境
Environment.initForCurrentUser();
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
//设置进程的参数
Process.setArgV0("<pre-initialized>");
//准备创建主线程的Looper
Looper.prepareMainLooper();
//创建ActivityThread对象
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
//获取主线程的Handler
sMainThreadHandler = thread.getHandler();
}
....
Looper.loop();//开始循环处理消息了
//如果执行到这里,说明主线程发生了异常,被异常终止了
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//获取当前线程的Looper,因为是在主线程中获取,所以是主线程的Looper
}
}
final Handler getHandler() {
return mH;
}
final H mH = new H();
private class H extends Handler {
}
从上面可以看到,在主线程中可以不用执行Looper.prepare()和Looper.loop()方法就可以创建Handler对象,因为在ActivityThread的main方法中,已经处理创建了Looper和MessageQueue,可以直接创建Handler对象。
6.总结
Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。
Looper负责创建一个MessageQueue,然后进入无限循环,不断从该MessageQueue中获取一个Message,将Message分发给对应的Handler处理。