我对四个重要的类的理解
1.Handler 负责发送(sendMessage) 和处理消息(handleMessage)
2.Message:消息载体包含消息id(what)、消息处理对象(obj)、Runnable接口等,Handler发送消息后,会将该消息加入到MessageQueue统一消息处理队列中
3.MessageQueue:消息队列.用来存放Handler发送的消息的队列,单列表的实现
4.Looper:消息泵,通过Looper.loop()创建一个死循环,不断地从MessageQueue中抽取Message,Message通过绑定的内部target(handler类型),msg.target.dispatchMessage(msg)将该消息传给对应的Handler接受.在dispatchMessage方法内调用handleCallBack或handleMessage方法.
Handler原理流程:
首先子线程获取数据之后会通过Message.obtion()封装成Message对象,然后主线程创建好的Handler会将Message发送到主线程的MessageQueue中,在创建Handler的时候内部构造还会创建一个Looper对象,创建Looper对象的同时又会创建一个MessageQueue对列这个对象,Looper会通过Looper.loop()方法会不断轮询Message中的Message交给Handler处理.
源码分析:
Handler源码
再创建Handler的时候,它的内部会通过Looper.mylooper()得到一个Looper对象,其实Looper.myLooper内部是通过ThreadLocal.get()方法获取的Looper对象,除此之外,还会通过looper创建一个MessageQueue对象然后赋值给全局的MessageQueue对象,这样还会通过looper创建一个MessageQueue对象然后赋值给全局的MessageQueue对象,这样Handler和Looper就共用一个MessageQueue,这样这三者就捆绑在一起了
Handler在发送消息的时候,不管是通过sendEmptyMessage()还是通过sendEmptyMessageDlayed()发送消息,最后消息都会走到enqueueMessage()方法中,在enqueueMessage()中将当前Handler对象赋值给了Message,target,这样就把Handler对象与Message绑定再了一起,那么在取Message的时候就可以取出与之绑定的Hanlder对象了.所以如果在当前线程中有多少个Handler对象的时候,就可以通过Message.target来获与之绑定的Handler,enqeueMessage()方法其实就是将Handler发送的消息添加到MessageQueue消息队列中.
MessageQueue源码
而这个MessageQueue中主要包含,插入,读取和删除操作,在MessageQueue中有个enqueueMessage()方法,这个方法就是向MessageQueue中插入消息,而MessageQueue中会通过个next()方法获取下个Message,会被Looper.Loop()方法不停的取出Message对象,MessageQueue底层其实是通过单链表的数据结构来维护Message对象列表的(如上图)
public static void loop() {
...//仅贴出关键代码
// 1.获取当前Looper的消息队列
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
Handler源码中的dispathMessage()和HandleMessage()
MessageQueue是通过当前线程的Looper对象来管理和创建的,在Looper的构造中会创建个MessageQueue对象(如上图Looper源码)因为Looper对象是通过ThreadLocal来保证当前线程中Looper的唯一性,所以Looper构造中的MessageQueue就是唯一的,另外Looper中通过Prerare()方法来创建当前线程的Looper对象,在prepare()方法内部其实是通过ThreadLocal来保存和获取这个Looper对象的,ThreadLocal 不是 Thread,它是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,对数据存储后,只有在线程中才可以获取到存储的数据,对于其他线程来说是无法获取到数据。也就是说每个Thread内可以通过ThreadLocal来创建变量的实例副本,且该副本只能由当前Thread可以使用,所以Threadlocal可以确保每个线程有唯一的一个Looper对象,Looper类中还有个loop()方法,其实内部是个死循环,在死循环内部通过queue.next()不停的从MessageQueue中轮询消息,如果没消息的话MessageQueue就处于阻塞状态,但是对当前主线程没影响,轮询出来消息之后会将Message交给Handler中的dispatchMessage()来处理,最后把Message交给在dispatchMessage()中的HandleMessage来处理。
总结有关Handlerd的问题?
1.在UI线程中有几个Looper对象?有几个MessageQueue对象?有几个Handler对象?有几个Message对象?
答:在UI线程中只有一个Looper对象,只有一个MessageQueue对象。但可以有很多个handler对象。可以有很多个Message对象。
2.怎么保证只有一个Looper对象的?
答:在Handler机制中使用Looper对象的地方有三个,一是在ActivityThread类中使用Looper.prepareMainLooper()创建Looper对象,二是在hanlder类中使用Looper对象,三是在Looper的loop方法中使用Looper对象处理消息。若这三个Looper对象是同一个就证明了在UI线程只有一个Looper对象。
Looper.prepareMainLooper()创建Looper对象
prepareMainLooper方法的原码是:
下面看prepare方法的原码:
分析:在第一次调用sThreadLocal.get()方法得到的一定是null,所以此时的重点是创建Looper对象,并放入sThreadLocal中储存起来。
这儿要明确两点:
1. sThreadLocal是Looper类的静态字段,所以只有一个sThreadLocal对象。
2. prepare方法在UI线程被调用,所以只有在Ui线程才能从sThreadLocal对象中获取到looper对象。
下面看myLooper方法的原码:
分析:这个方法很简单,目的就是得到UI线程中的Looper对象。注意这个方法是静态方法,得到的Looper对象就是在UI线程中创建的Looper对象
总结:Looper通过prepare()方法来new出Looper对象,然后将Looper对象存入当前线程的ThreadLocal中,Looper.prepareMainLooper()实际上是调用了Looper.myLooper()方法,然后在MyLooper()内部其实也是通过ThreadLocal,get()方法获取到的Looper对象
3.怎么保证只有一个MessageQueue对象的?
答:首先我们找一下MessageQueue对象是在哪儿创建的?我们知道在在Looper的prepare方法中创建了Looper对象,并放入到ThreadLocal中,具体代码是:
那么我们来看一下Looper的构造方法:
在Looper的构造方法中创建了MessageQueue对象,并赋值给mQueue字段。因为Looper对象只有一个,那么Messagequeue对象肯定只有一个。
4.为什么发送消息在子线程,而处理消息就变成主线程了,在哪儿跳转的?
答:发送消息使用的是Handler的sendMessage方法,这个方法最终调用的是enqueueMessage方法,enqueueMessage方法的原码是:
这个方法中调用的是MessageQueue的enqueueMessage方法,因为queue对象是UI线程中的MessageQueue对象,那么与之对应的Looper就是UI线程的,UI线程的Looper从消息队列中取出来,然后就在主线程执行了,所以在处理消息时就是主线程了
5.looper对象只有一个,在分发消息时怎么区分不同的handler?
答:一是Message类中有个字段target,这个字段是Handler类型的二是在Handler的enqueueMessage方法中有这么一句代码:msg.target = this;即把handler对象与Message绑定在了一起。三是在Looper类的looper方法中分发消息的代码是:msg.target.dispatchMessage(msg)
所以在发送消息时handler对象与Message对象绑定在了一起。在分发消息时首先取出Message对象,然后就可以得到与它绑定在一起的Handler对象了
6.能不能在子线程中创建Handler对象?
答:在UI线程之所以可以直接创建创建Handler对象,是因为在UI线程中默认有个Looper对象,在子线程中创建Looper对象的话示例如下:
7.怎么在子线程中得到主线程中handler对象?
在Looper类中有getMainLooper方法,这个方法的源码是:
这个方法返回的是在主线程中创建过的Looper对象。所以这个方法无论在哪儿调用得到的都是主线程中的Looper对象,此时我们就可以在子线程中创建主线程的handler对象了
网友评论