Android版本: 基于API源码26,Android版本8.0。java
本片文章的目的在于全面的了解Handler。它是如何传递消息的?是如何阻塞和唤醒线程的(仅限于Java层面)?MessageQueue究竟是怎么存储和取出Message?延迟消息是怎么被发送的?android
Handler是一套消息传递的机制。设计用来让工做线程跟主线程之间进行消息传递。同时也解决了多线程同时更新UI的问题。Android系统设定子线程是不能更新UI的,当子线程必定要作更新UI的操做时,可使用Handler将消息从子线程带到主线程,并能保证消息的同步性。git
在主线程中使用:github
//实例化Handler对象。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
//建立并发送消息
Message obtain = Message.obtain();
obtain.what = 100;
handler.sendMessage(obtain);
复制代码
在子线程中使用,一旦使用该子线程是不会自动结束的,除非手动结束:json
//为子线程中建立Looper对象。
Looper.prepare();
//实例化Handler对象。
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
//建立并发送消息。
Message obtain = Message.obtain();
obtain.what = 100;
handler.sendMessage(obtain);
//开启消息循环。
Looper.loop();
复制代码
若是先调用Looper.prepare()、Looper.loop()再handler.sendMessage(obtain),那么消息会成功的发送成功吗?api
Handle的消息传递过程当中涉及到了几个重要的类:Handler、Message、Looper、MessageQueue。缓存
Handler安全
官方的解释:Handler容许您发送和处理Message
和MessageQueue
有关联的可运行对象Runnable。每一个Handler实例都与一个线程和该线程的消息队列关联,在建立的时候就会直接关联起来。Handler将Message和Runnable传递到消息队列中,并在它们从消息队列中出来时执行它们。多线程
Handler被用在两个主要的地方:让Message和Runnable定时执行;在非UI线程中将Message或者Runnable发送到主线程。并发
当应用程序建立进程时,其主线程专用于运行消息队列,该队列负责管理顶级应用程序对象(activities、broadcast receivers等)及其建立的任何窗口。
也就是Handler是用来专门发送和处理消息的。发送是只发送到对应的消息队列就行了。
Message:
官方解释:定义包含描述和可发送到{@link handler}的任意数据对象的消息。此对象包含两个额外的int字段和一个extra对象字段,在许多状况下容许您不进行分配。
也就是Message是Handler传递的元数据,其内部能够包含不少信息:what、arg一、arg2,、obj等。Handler发送的也是Message对象,处理的也是Message对象。因此Message确定是序列化的,继承自Parcelable。内部维护了一个缓存池,避免建立多余的消息对象,缓存池的设计是Android系统源码一向的做风,在ViewRootImpl处理点击消息的时候,也有相似设计。该缓存池的大小为50,超过该缓存就会直接建立Messsage对象。
Looper:
官方解释:Looper类用于为线程运行消息循环。默认状况下,线程没有与之关联的消息循环;若要建立一个消息循环,请在要运行该循环的线程中调用 prepare()方法,而后调用loop()使其处理消息,直到循环中止。
也就是Handler只是将Message数据放到了MessageQunue中,Looper才是从MessageQunue中取出消息,并将消息发送到正确的Handle中去。一个线程中存在一个Looper,Looper中维护着MessageQueue。Looper.prepare()方法是建立Looper跟MessageQueue,再发送消息以后调用Looper.loop()方法,开启消息循环,也就是不停的从消息队列中那出消息处理。
MessageQueue:
该机制中核心的存在。主要是用来操做Message的,实际上内部并无一个集合或者队列去存储Message,而是由Message组成的单链表结构。在Message的内部有个next属性,属性声明为Message对象,也就是Message自身就可组成一个队列。MessageQunue的目的就是增删对比Message。好比:第一个Message对象来了(m1),那么它的next属性是空的,而后又来了一个Message(m2)。发现m1还没执行完毕,那么就将m2的对象赋值给m1的next属性,当m1执行完毕以后,取出本身的next属性,若是不为null,那就接着执行next属性表示的Message,也就是m2。
当了解完每一个类的职责以后,发送的消息的流程就会清晰的不少。
通常的,一个Thread应该只有一个Looper和MessageQueue。能够有多个Handler对象。当你在主线程中建立Handler的时候,你是不须要调用Looper.prepare()的,由于主线程已经建立过了。建立Looper的线程决定了消息最终是在那个线程中被处理的。你在子线程中使用Handler对象发送消息,若是你的Looper是主线程中建立的,那么处理消息的仍是在主线程中,你发送消息的线程环境是可有可无的。
Handler建立源码解析:
Handler类是个普通的Java类,能够被继承。建立方式为:
//通常经过new的方法去建立。
Handler handler = new Handler();
复制代码
Handler有七个构造方法,三个是被隐藏掉的。被注解@hide注释以后,经过api是访问不到的:
//Handler.java
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/** * @hide */
public Handler(boolean async) {
this(null, async);
}
/** * @hide */
public Handler(Callback callback, boolean async) {
//。。。省略代码
//获取Looper对象。
mLooper = Looper.myLooper();
//。。。省略代码
//获取MessageQueue。
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
/** * @hide */
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
复制代码
能够看到构造参数中能够传入的类型:Callback接口,Looper对象,布尔值async。首先Callback接口也是处理消息的:
//Handler.java
public interface Callback {
/** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */
public boolean handleMessage(Message msg);
}
复制代码
Handler类内部也有handleMessage()方法,使用的时候彻底能够重写该方法。Handler又提供该接口的目的也就是你能够不用实现Handler类中的方法也能处理消息,这样自由一点。
若是构造方法中你传入了Looper对象,就不须要去建立Looper对象了。至于布尔值async则是决定当前发送的Message是不是异步的消息,异步消息能够不受同步屏障的影响。在ViewRootImpl接收到绘制信号的时候,就会在UI Handler中发送一个同步屏障,这将阻止当前Handler处理全部的同步消息,而后让该此异步消息先执行。Handler的建立过程很简单,接下来看Message的建立过程。
Message建立源码解析:
Message类是final的不能被继承。实现了Parcelable接口。Message并无特殊的构造方法,因此直接就可new出来:
Message message = new Message();
复制代码
另外系统推荐的获取Message的方法为:
Message obtain = Message.obtain();
复制代码
obtain()方法有不少的重载方法,根据参数的不一样构造不一样的Message对象。该过程都是在Message内部实现的,对于调用着来讲是透明的,下面来分析下基础的obtain()方法:
//Message.java。
public static Message obtain() {
synchronized (sPoolSync) {
//判断缓存池是否为null。
if (sPool != null) {
Message m = sPool;
//取出一个Message。
sPool = m.next;
m.next = null;
//默认的flag为0。
m.flags = 0; // clear in-use flag
//大小减一。
sPoolSize--;
return m;
}
}
//不然就new一个
return new Message();
}
复制代码
sPool是一个Message对象,至关于缓存队列的头指针。Mesage有个next属性,该属性仍是Message对象。当调用该方法的时候,就从sPool中取出下一个Message也就是m.next,而后将m.next赋值给sPool,也就是重置队列头,而后返回以前的队列头m。这种是典型的单向链表结构,Android系统的源码中不少地方都用到。而后在Message的reaycle()方法中,将使用过的Message存放到sPool表明的队列中。
//Message.java。
public void recycle() {
if (isInUse()) {
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
//。。。省略代码
synchronized (sPoolSync) {
//判断缓存的数量是否小于规定值的
if (sPoolSize < MAX_POOL_SIZE) {
//将队列头指针指向下一个元素
next = sPool;
//队列头重置,这样新的消息就变成了队列的第一个元素,以前sPool表示的消息就变成该消息的next。
sPool = this;
sPoolSize++;
}
}
}
复制代码
Message的建立很简单,须要注意的就是这个缓存池的原理。
消息发送流程源码解析:
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
Message obtain = Message.obtain();
obtain.what = 100;
handler.sendMessage(obtain);
复制代码
上述代码便可发送一个消息数据。无论是sendMessage()仍是sendMessageDelayed()或者sendEmptyMessage()方法,最终都是调用的sendMessageAtTime()方法,sendEmpty***系列是Handler本身建立了一个空的Message,只是在使用着看起来是发送了一个空的消息同样,这里再也不分析:
//Handler.java。
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);
}
复制代码
该方法中只是判断了MessageQueue是否为null。
//Handler.java。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//该消息的目标Handler为当前发送消息的Handler。
msg.target = this;
//若是是异步消息的话就设置为异步标记。
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
对Message对象作了一些标记以后就调用了MessageQueue的enqueueMessage()方法,这两个标记都是很重要的,一个是设置目标Handler,这样消息就不会让错误的Handler处理。另一个是代表消息是不是异步的,通常使用的时候都是同步消息,UI线程毕竟是线程不安全的:
//Message.java。
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
复制代码
接着看queue.enqueueMessage()方法:
//MessageQueue.java。
boolean enqueueMessage(Message msg, long when) {
//若是消息没有目标Handler就抛出异常。
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) {
//当前的消息队列已经结束了。mQuitting = true。
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;
//mMessages表示当前准备处理消息。
Message p = mMessages;
boolean needWake;
//1.当前要处理的消息为null。2.须要立刻发送,延迟时间为0。3.若是当前要处理的消息的延迟比正在处理的消息时间段。
//上面三个条件知足一个就执行。若是是第一条消息if确定会执行,若是来了第二条Message,可是第一条Message还没执行完,也就是mMessages不为null,消息的when还等于0,那么也要直接执行,也就是添加到队列的头部。因此markInUse()方法仍是颇有必要的。
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 {
//当下一条消息来的时候发现前面还有消息没执行完,而且当前的消息也不是立马就执行的,或者等待的时间比正在执行的消息要长,那么就执行到这边。
//p.target == null表示当前的消息是一个同步屏障。
//needWake = true,说明当前的前一条消息是个同步屏障,而且当前的这条消息是个异步消息。
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);
}
}
return true;
}
复制代码
上面的入对列操做很复杂:
通过上面的步骤,Handler将发送的Message放入到了消息队列中。Handler类的职责已经结束了。下面来看Looper到消息队列中取数据。
Looper源码解析:
Looper类的注释中说明了,在使用的时候要先调用Looper.prepare()方法,最后调用Looper.loop()方法。下面先看下prepare()方法:
//Looper.java。
//该方法是能够在任何线程中调用。
public static void prepare() {
prepare(true);
}
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));
}
//该方法在ActivityThread建立的时候会被调用。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
复制代码
prepare()系列方法就是建立一个Looper对象。采用prepareMainLooper()方法建立的Looper不会终止消息循环,其余的均可以被终止掉,也就是quitAllowed = true/fasle的问题。Looper建立结束以后看下loop()方法:
//Looper.java。
public static void loop() {
final Looper me = myLooper();
//。。。省略日志代码
//Handler跟Looper用的是同一个队列。
final MessageQueue queue = me.mQueue;
//确保此线程的标识是本地进程的标识,并跟踪该标识令牌的实际内容。
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;
}
//。。。省略日志代码
try {
//msg.target就是Handler,而后分发消息。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
//。。。省略日志代码
}
//。。。省略日志代码
//消息处理结束以后回收消息。
msg.recycleUnchecked();
}
}
复制代码
loop()方法要作的就是不停的从MessageQueue中获取消息,因此就使用了For死循环。拿到消息以后,开始分发消息,最后将消息回收。重点在于Message的next()和Handler的dispatchMessage()以及最后Message的recycleUnchecked()方法了。先看next()方法:
//MessageQueue.java
Message next() {
//若是消息循环已经退出并被释放,则返回此处。若是应用程序在退出后尝试从新启动不受支持的循环程序,则可能发生这种状况。
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
//空闲时待处理的IdleHandler的数量。默认为-1。
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//下次轮询超时毫秒
int nextPollTimeoutMillis = 0;
//开启死循环。要么阻塞,要么获取到Message。
for (;;) {
//将当前线程中挂起的任何绑定器命令刷新到内核驱动程序。在执行可能会阻塞很长时间的操做以前调用,以确保已释听任何挂起的对象引用,以防止进程占用对象的时间超过所需时间,这将很是有用。
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//该方法可能会被阻塞,nextPollTimeoutMillis = -1的时候,将无限期的阻塞。也就是说当该方法有返回值的时候,那就表明该过程不会被阻塞,也就是会执行下面的代码逻辑,也就是将会至少取出一个消息。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 获取系统时间。
final long now = SystemClock.uptimeMillis();
//该Message是表示将被执行的消息的前一个消息,有多是从消息队列的中间取出一个消息执行,这样须要将被取出消息的next拼接到被取出消息的上一个消息的next上,这样队列不会被断开。
Message prevMsg = null;
//队列的头消息。每次取出一个消息的时候都会将mMessages指向新的消息。
Message msg = mMessages;
if (msg != null && msg.target == null) {
//执行到这里说明当前的消息是一个同步屏障,也就是将阻止全部的同步消息。
//找出队列中的第一个异步消息,没有异步消息msg就是null。
do {
//找出消息的上一个消息。
prevMsg = msg;
//当前找出的消息。
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//这里并非else语句,因此就是否是同步屏障就直接执行到这里。
if (msg != null) {
if (now < msg.when) {
// 下一条消息还没有准备好。设置一个超时,以便在它准备好时唤醒。也就是nextPollTimeoutMillis参数其实就是上面nativePollOnce()本地方法的参数,也就是阻塞的时长。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//走到这里说明找到了符合要求的Message,准备返回了。
mBlocked = false;
if (prevMsg != null) {
//prevMsg不为null表示当前的msg是一个异步消息,那么其值表示msg消息的上一个消息。也就是msg是从队列中间取出来的,那么就须要把队列拼接完整了。
prevMsg.next = msg.next;
} else {
//走到这里说明msg是一个同步消息,也是从队列的头部获取到的,那么就重置mMessages为msg的下一个消息。
mMessages = msg.next;
}
//清空该消息的next属性,这样该消息就是一个独立的消息。在上一步中该消息的next属性已经赋给了队列中的其它Message。
msg.next = null;
//标记正在使用
msg.markInUse();
return msg;
}
} else {
// 没有消息就为-1.-1就表示该线程将无限期的阻塞。
nextPollTimeoutMillis = -1;
}
//处理完全部挂起的消息后,当即处理退出消息。
if (mQuitting) {
dispose();
return null;
}
//。。。省略了空闲时处理IdleHandler的逻辑。IdleHandler对象,IdleHandler是一个接口,也就是继承了该接口的事物将会在MessageQueue空闲的时候去处理。GC机制就是采用的这个东西触发的。
if (pendingIdleHandlerCount <= 0) {
//没有东西能够执行了,就开始等待工做。
mBlocked = true;
//continue继续执行for循环,而后在执行nativePollOnce()方法,线程阻塞。
continue;
}
}
}
复制代码
在Looper的loop()方法中第一个重要的就是MessageQueue的next()方法。该方法有如下几点:
msg.target == null
,是的话就要拿出队列中第一个异步消息,也就是Message的isAsynchronous()方法返回true。若是不是同步屏障,就继续往下走。上述就是MessageQueue的next()方法。通过该方法后继续看Looper的loop()方法:
//Looper.java。
public static void loop() {
//。。。省略代码
for (;;) {
//。。。省略代码
try {
//msg.target就是Handler,而后分发消息。
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
//。。。省略日志代码
}
//。。。省略日志代码
//消息处理结束以后回收消息。
msg.recycleUnchecked();
}
}
复制代码
这里获取到Message的target属性,而后调用了dispatchMessage()方法。target属性是在Handler的enqueueMessage()方法中赋值的:
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
也就是当前Handler对象。以后就是Handle的分发方法:
//Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
代码一目了然。若是msg.callback不为null,说明当前的消息是Runnable对象。不是的话,判断mCallback是否为null,也就是Handler的构造方法中的参数Callback,不为null就调用Callback的handleMessage()方法。再接着就是调用自身的handleMessage()方法。到这Handle的源码分析就结束了!重点难点全在MessageQueue类中。