-
Handler源码浅析
讲到Android消息机制,主要就是Handler了,从字面上,就是处理者的意思,处理其他线程发来的消息,完成比如UI更新这样的任务。然而单靠一个Handler就能独挑大梁吗?不是的,整个消息机制的运行,还需要消息队列MessageQueue,Looper这俩辅助的配合,具体是怎么配合的呢,下面尽可能地一气呵成描述清楚。
以主线程更新UI为例,我们在主线程创建了Handler
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
当我们写完以上这段代码,系统已经帮我们做完了几件事,什么事呢,直接进入Handler的构造方法中看看:
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper(); //获得handler所在的线程的looper
if (mLooper == null) {
throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //取出Looper中的消息队列 初始化mQueue
mCallback = callback;
mAsynchronous = async;
}
Handler的无参构造方法最后实际是调用到上面这个构造方法,这边把一些暂与本文主题无关的代码剔除了,留下关键的代码。首先我们看到,先是调用myLooper( )获取到一个Looper对象,myLooper( )里是直接new了个Looper出来吗?不是的,是从一个叫ThreadLocal里取出来的,有取就肯定先存了,什么时候存的呢?这部分放到拓展里去,免得破坏描述思路。接下来马上再通过Looper对象初始化一个消息队列出来mQueue,Looper是什么玩意?字面上loop加上er,英语八级的你应该知道加个er就是~者的意思,所以就是循环者,具体点叫消息循环者,看下官方文档的定义:
Class used to run a message loop for a thread
专门用来驱动一个线程上的消息队列循环,handler发送里的message都会被这个Looper进行循环处理,具体后文。
接着这个mQueue是在Looper的构造方法里初始化的,所以一个Looper是绑定了一个MessageQueue的:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //初始化消息队列
mThread = Thread.currentThread();
}
这样,我们前面所说的另外两个辅助(Looper和MessageQueue)都在Handler里初始化好了,静候待命。
接下来就是其他工作线程发送消息
Message message = new Message();
message.arg1 = 1;
handler.sendMessage(message);
sendMessage最终是调用到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("MessageQueue", 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;
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;
}
}
return true;
}
以上就是消息入队的逻辑,也就是单链表的插入操作,按时间顺序将消息进行排序等待处理。到这里,handler.sendMessage( )就完成了消息的入队操作。不是,那这边就有个疑问了,通过sendMessage怎么就能把消息给发送到主线程呢,然后待命的那些伙计们又怎么开始干活(处理消息)呢,还是得带着问题进入到源码看看,这时候待命中的Looper上场了,Looper有个核心方法,就是loop(),来看看loop方法里的执行逻辑。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
再次剔除暂无关代码,看到loop方法里,先是取出当前线程的Looper,然后再拿到Looper所绑定的消息队列,接着就是一个死循环,取出msg后,调用target的dispatchMessage方法,这个targit是什么呢,其实就是handler在发送消息的时候,就将自己封装到MessageQueue里,成为它的一个变量targit,然后dispatchMessage里有个方法handleMessage(msg);真相大白了,最终就是这样回到handler的handlerMessage里,整个消息机制大概模型就是这样了。
-
拓展
为了不在描述的时候跑偏,当中有些概念放到了拓展中来解释。
1.首先是ThreadLocal是个啥玩意儿?线程内部的存储类,在消息机制中,它主要用来Looper的存取set和get,这样保证了取的是当前线程的Looper;
2.如果是在主线程存取这个Looper,存的话系统一开始就已经调用Looper.prepare为我们存了,不需再操作;如果是在子线程,则需要调用Looper.prepare( )来把Looper存到ThreadLocal中,当然子线程创建Handler可以直接使用另一个封装的类 HandlerThread
HandlerThread其实就是一个继承于Thread的线程,一些耗时操作不需要我们一直去new Thread(),直接发给它去处理,处理完还可以再发出来给主线程,它的特点就是内部已经帮我们初始化好了Looper对象Looper.prepare();mLooper = Looper.myLooper();Looper.loop();Looper三连,由上文可知,Looper就是要用来关联一个消息队列,然后开启loop取消息。看下怎么创建HandlerThread(前方kotlin乱入)
val handlerThread = HandlerThread("handler")
handlerThread.start()
//创建关联handlerThread 的handler
val handler = object : Handler(handlerThread.looper){
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
//执行耗时操作,随时也可以跟UI线程通信
mainHandler.post({
text.text ="收到消息啦"
})
}
}
button.setOnClickListener({
val msg1=Message()
msg1.obj="1"
//发消息到Handler子线程
handler.sendMessage(msg1)
})
4.说到HandlerThread,还要顺便提上IntentService,这也是谷歌为我们封装的异步任务处理类,内部创建了HandlerThread来处理我们的耗时操作,有一点是它一次只能处理一个任务,如果需要并发,则需要使用上线程池。
4.网上看到个比较钻牛角尖的问题,Android中为什么主线程不会因为Looper.loop()里的死循环卡死?也有底层比较了解的回答了,回答直通车知乎回答总的来说,就是由linux的epoll决定的,没消息的时候wait,消息来的时候唤醒,就不会出现ANR
-
总结
当在一个线程中创建了Handler,随之也会在该线程初始化一个Looper和MessageQueue,其他线程发送的message最终都入队到你创建Handler的线程中来,因为要到这里的MessageQueue入队,Looper的loop方法会不断地从MessageQueue中取出消息,交给Handler去处理。
-
最后
如今这个时代,知识如同一波又一波的海浪无穷无尽,旧的知识很快又会被新的知识替代,学过一样东西,怎样才能记住更久,我想将学过的东西复述一遍,这是个不错的方法,毕竟你能转述一遍,说明是有学进去些的。当然转述的过程,可能还是个人认知水平的体现,如有差错,还望指出。