本篇文章基于AndroidQ分析Handler消息机制java层的实现
主要从message消息池,handler消息投递,Looper通过loop方法向MessageQueue获取消息,handler消息分发顺序,MessageQueue的同步屏障几方面分析
Handler消息机制一般用于线程间的通信,A线程可以通过消息机制向B线程发送消息,只要获取到B线程的Looper对象,我们用的最多的就是在子线程获取数据之后在主线程更新UI,我们从一个简单的例子开始源码分析:
public class MainActivity extends Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("dongjiao","handleMessage...msg.what = :"+msg.what);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 10;
mHandler.sendMessage(message);
}
}).start();
}
输出:
02-17 09:04:15.138 15536 15536 D dongjiao: handleMessage...msg.what = :10,
currentThread = : Thread[main,5,main]
这种handler的用法只要学过几天Android应该都会用,我们接下来通过源码来分析它的具体实现。
消息机制主要由四部分组成(Handler,Message,MessageQueue,Looper),Handler:向MessageQueue投递Message,并且进行Message的处理
Message:作为消息的载体,携带具体数据
MessageQueue:一系列Message的容器
Looper:内部对MessageQueue进行无限循环,有消息就取出交给Handler处理,没消息就阻塞
Message消息池
Android系统以消息来驱动,内部会有大量的消息,为了节约内存以及提高效率采用消息池对Message进行复用,从上面例子我们可以看到,Message采用obtain来创建而不是new,来看下源码
Message.java
Message实现了Parcelable接口表明Message不仅能在线程间传递还能跨进程传输
public final class Message implements Parcelable {
//当Handler发送多个消息时,用作不同消息处理的区分
public int what;
//当你传输的数据仅仅是int型时优先使用arg1,arg2
public int arg1;
public int arg2;
//Message携带的数据
public Object obj;
//Message执行的延迟时间
public long when;
//处理此Message的目标Handler
Handler target;
//Message的回调,如果设置了此回调,handler的handleMessage方法
//将不会被执行
Runnable callback;
//Message采用链表的形式进行复用,next指向下一个消息
Message next;
//Message消息池的头指针
private static Message sPool;
//Message消息池的大小
private static int sPoolSize = 0;
}
obtain这个方法有很多的重载方法,我们选择最常见的无参方法来分析
Message.obtain
public static Message obtain() {
synchronized (sPoolSync) {
//如果sPool不为空,则从消息池取一个Message
if (sPool != null) {
//从消息池中取出头部这个消息
Message m = sPool;
//sPool继续指向下一个消息
sPool = m.next;
//将取出的消息的链断掉
m.next = null;
//修改此消息的flag为0
m.flags = 0; // clear in-use flag
//消息池大小减1
sPoolSize--;
return m;
}
}
return new Message();
}
sPool总是指向消息链表的头部,首次创建Message时sPool为空,就需要通过new Message的方式创建Message,我们看下sPool首次是在哪里赋值的,是在recycleUnchecked中,看名字以及注释我们知道此方法是Message使用完毕之后会调用,也就是说我们创建的第一个消息使用完毕之后,会赋值给sPool
void recycleUnchecked() {
//当Message使用完成之后会将Message的一系列数据初始化
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//MAX_POOL_SIZE为50,作为消息池的大小,当sPoolSize没有到50时,
//则此消息应该加入消息池
if (sPoolSize < MAX_POOL_SIZE) {
//如果是第一次消息回收next = sPool = null
next = sPool;
//将当前这个消息赋值给sPool,我们可以看到sPool总是指向
//最后调用recycleUnchecked方法的Message
sPool = this;
//消息池大小加1
sPoolSize++;
}
}
}
从obtain和recycleUnchecked方法中我们能够看出来Message内部消息池采用链表的形式,sPool指向链表的头,每次取出消息是从链表头部开始,每次消息回收也是加在链表头部
Message消息的投递
接着我们来看下Handler如何向MessageQueue投递消息,首先看下Handler的构造方法,例子中使用Handler无参构造最终调用到如下这个构造方法中
public Handler(@Nullable Callback callback, boolean async) {
//获取当前线程的Looper对象,后面分析
mLooper = Looper.myLooper();
//如果Looper为空会抛如下这个异常,这个异常相信Android初学者
//遇见过很多次
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//获取当前线程Looper的MessageQueue
mQueue = mLooper.mQueue;
//Handler内部回调
mCallback = callback;
//是否是异步消息,后面分析
mAsynchronous = async;
}
Handler.sendMessage
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//msg.target代表处理此消息的Handler
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//是否是异步消息,后面分析
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//最终调用MessageQueue的enqueueMessage将此Message投递进MessageQueue
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler投递消息很简单,就是一系列sendXXX,调到了MessageQueue中,不管是哪个sendXXX方法,最终一定会调到MessageQueue.enqueueMessage中
Looper以及MessageQueue的创建
我们知道UI线程不需要我们手动创建Looper,原因是每个应用程序进程创建时就已经创建了Looper并开启了消息循环,在ActivityThread的main方法中
public static void main(String[] args) {
Looper.prepareMainLooper();
.......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
......
Looper.loop();
}
Looper.prepareMainLooper
public static void prepareMainLooper() {
//注意UI线程调用prepare时传递false,代表不允许Looper退出
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepare
此方法很简单,通过ThreadLocal保存当前线程的Looper对象,ThreadLocal用来存储线程私有变量,起到线程隔离的目的,具体原理我们这里不去分析,用法非常简单,到这里我们知道每个线程自己创建的Looper对象一定只能由自己线程访问
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));
}
Looper构造方法里面创建了MessageQueue,并保存了当前线程
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
MessageQueue构造方法中调用了nativeInit方法做了很多事,此方法我们在下一篇对消息机制native层的分析中去细看
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
好了,到这里Looper以及MessageQueue的初始化已经完成,MessageQueue已经创建了,我们可以看下MessageQueue内部是如何保存Message的
MessageQueue.enqueueMessage
MessageQueue内部的消息其实也是通过链表的结构存储的,和Message消息池类似
boolean enqueueMessage(Message msg, long when) {
//如果有message而没有Handler则抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//如果此Message正在使用中则抛出异常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//如果MessageQueue正在退出则抛出异常
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;
}
//将此Message添加flag,代表正在使用
msg.markInUse();
//when代表此Message执行的延迟时间
msg.when = when;
//mMessages指向Message消息链表的头部
Message p = mMessages;
//是否需要唤醒
boolean needWake;
//p == null代表此时没有消息,when == 0代表此消息需要立即执行
//when < p.when代表新增加的消息比此时MessageQueue中的消息头
//的消息还要先执行(消息队列的顺序是when最小排在头部,when最大排在尾部)
if (p == null || when == 0 || when < p.when) {
//所以如果进到此条件则代表增加的这条消息是最先执行的,应该加到
//消息队列头部
msg.next = p;
mMessages = msg;
//将当前MessageQueue的状态赋值给needWake,以便能在阻塞时唤醒
//MessageQueue
needWake = mBlocked;
} else {
//p.target == null && msg.isAsynchronous()代表是否开启
//同步屏障,后面分析
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//循环整个Message链表,将此新增的消息插入到合适位置
for (;;) {
prev = p;
p = p.next;
//当找到p == null代表已经到链表尾部,或者下一个msg的when
//大于当前新增消息的when
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//链表增加元素的常规操作,断开链next,将msg插入进去
msg.next = p;
prev.next = msg;
}
//唤醒MessageQueue依靠native层
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
enqueueMessage方法其实就是一个链表的插入操作,规则是按照msg的执行时间排序,时间越短越靠近头部
我们接着分析Looper对消息的循环取出
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;
//死循环的获取MessageQueue的消息
for (;;) {
//获取消息的核心方法,
Message msg = queue.next(); // might block
//如果获取到的msg为空则说明MessageQueue已经退出,则Looper也需要
//退出循环
if (msg == null) {
return;
}
//待queue.next()方法分析完之后再接着分析后面代码
......
......
}
MessageQueue.next
Message next() {
//mPtr保存native层MessageQueue的指针
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//nextPollTimeoutMillis代表当前消息的执行时间,0代表立即执行,-1代表
//此时MessageQueue没有消息
int nextPollTimeoutMillis = 0;
//无限循环的取出消息
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//此方法是消息机制的核心,会在下一篇native层消息机制进行分析
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//获取当前时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//前面分析过,mMessages指向消息链表头部
Message msg = mMessages;
//如果msg不为空handler却为空说明此时Looper需要获取异步消息
if (msg != null && msg.target == null) {
do {
//典型的链表循环
prevMsg = msg;
msg = msg.next;
//退出条件是找到设置了Asynchronous的消息或者
//直到链表尾部也没找到,说明此链表不含异步消息
} while (msg != null && !msg.isAsynchronous());
}
//msg不为空代表此消息链表中是有消息的
if (msg != null) {
//还没到消息执行时间
if (now < msg.when) {
//则重置nextPollTimeoutMillis为等待执行的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {//需要取出消息开始执行
//将mBlocked置为false代表MessageQueue不需要阻塞
mBlocked = false;
//prevMsg指向异步消息前一个消息,不为空代表有异步消息
if (prevMsg != null) {
//链表取出消息的常规操作
prevMsg.next = msg.next;
} else {
//如果没有异步消息,则取出消息队列头部的消息,并让
//mMessages继续指向下一个消息
mMessages = msg.next;
}
//断开取出消息的链
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//标记此消息为正在使用
msg.markInUse();
//返回取出的消息
return msg;
}
} else {
//消息队列中没有消息
nextPollTimeoutMillis = -1;
}
//如果消息队列退出
if (mQuitting) {
dispose();
return null;
}
//省略的代码是关于pendingIdleHandler的处理,这个handler会在
//MessageQueue为空时执行..不去细看了
.....
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
我们已经分析完了Handler消息机制中最重要的消息循环与获取的流程,做一个总结:
- Message在MessageQueue中以链表的形式存在,并且是按when执行时间从小到大排序,mMessages总是指向消息链表头部
- Loop会无限循环MessageQueue,有消息就取出,没有消息就阻塞(阻塞的核心实现在native层,我们下一篇进行分析)
- MessageQueue中有一种异步消息,它的优先级高于同步消息(后面分析)
- 当消息队列为空时会执行pendingIdleHandler
我们继续回到Looper中看去到Message后的处理流程:
public static void loop() {
......
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
//省略了很多log相关代码,主要帮助开发者实现监听UI线程卡顿
......
try {
//msg取到之后调用了msg的target也就是handler的dispatchMessage
//方法进行消息处理
msg.target.dispatchMessage(msg);
}
.......
//msg的回收复用,前面已经讲过,此方法会将使用过的msg加入消息池
//进行复用
msg.recycleUnchecked();
}
}
handler.dispatchMessage
我们看到,handler处理消息有一个优先级,首先msg的callback,其次handler的mCallback,最后才是handler的handleMessage方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
看一个实际使用的优先级:
1. hanlder.post(runnable);
2. private Handler mHandler2 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
3.private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
handler异步消息
最后我们再来看看异步消息,在Android源码绘制流程中使用了异步消息,目的是尽可能快的完成View的绘制
ViewRootImpl.java
void scheduleTraversals() {
......
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
.....
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
Choreographer.java
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
......
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
......
synchronized (mLock) {
.....
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
先调用postSyncBarrier开启同步屏障,屏蔽同步消息,接着设置setAsynchronous为true,将此消息设置为异步,则优先处理异步消息
同步屏障原理
我们从handler投递msg的分析知道,无论那种投递方式,只要是通过handler的sendXXX方法,最终一定会调用到enqueueMessage方法,
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
此方法第一行代码要做的事就是msg.target = this,而从MessageQueue.next方法中,我们知道要优先处理异步消息有两个条件,一是**(msg != null && msg.target == null),然后才循环消息链表,找到msg.isAsynchronous()为true的msg**,
Message next() {
......
for (;;) {
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
}
......
}
我们可以得出结论,同步屏障就是向MessageQueue投递一个handler为空的msg,有了这个msg,从此msg开始,之后的所有同步消息都会被屏蔽,只会处理isAsynchronous为true的异步消息
msg.setAsynchronous(true)
设置FLAG_ASYNCHRONOUS标示此msg为异步消息
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
开启同步屏障:
MessageQueue.postSyncBarrier
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
//创建没有handler的msg
final Message msg = Message.obtain();
msg.markInUse();
//时间是当前系统时间
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
//找到晚于当前时间的msg
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
//将新创建的handler为空的msg插入到prev后面
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
//说明msg应该插到消息头部
msg.next = p;
mMessages = msg;
}
return token;
}
}
我们总结下使用异步消息,两个条件:
- 开启同步屏障即投递一个handler为空的msg
- 将需要发送的msg设置为异步即调用setAsynchronous(true)
并且从源码中我们知道同步屏障只能屏蔽消息链表中添加空handler的msg之后的同步消息
好了,到这里本篇Handler消息机制(java层)就已经分析完毕,主要从源码分析了message消息池,handler消息投递,Looper通过loop方法向MessageQueue获取消息,handler消息分发顺序,MessageQueue的同步屏障。
另外handler的核心:阻塞与唤醒依靠native层进行实现我们将在下一篇**handler消息机制(native层)**进行详细分析