一、Handler原理
了解其原理首先需要了解一下4个类。
- Handler
(1)负责从子线程(或主线程)发送消息到消息队列(通过sendMessage等方法)
(2)处理消息队列返回的消息对象(通过handlerMessage方法)
- Message
消息对象(包含what、obj、当前handler等属性)
- Looper
负责从消息队列中不断的取出消息,交给handler处理(通过Looper.loop方法)
- MessageQueue
消息队列,存储消息。
1、源码分析,通过ActivityThread.class。进入该类的main方法,代码如下:
public static void main(String[] args) {
。。。
Looper.prepareMainLooper();
。。。
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2、跟踪prepareMainLooper(),代码如下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
3、进入prepare()方法,代码如下:
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));
}
sThreadLocal之前的handler中已经讲过,它的key就是当前线程,value是Looper对象。所以我们不能重复的调用prepare方法,否则就会抛出异常。另外线程第一次调用该方法就会创建一个Looper对象,保存在threadLocal中,是全局唯一的。
4、进入Looper的构造方法,代码如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的构造方法中会创建一个MessageQueue消息队列对象。由于一个线程中Looper只能被初始化一次,所以一个线程对应一个Looper,一个Looper对应一个消息队列MessageQueue。
5、当我们发送消息,以调用sendMessage为例,其实无论是哪种发送消息的方式,最终都会进入enQueueMessage方法,代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里重点注意 msg.target = this;这里的msg.target就是handler对象。最后通过调用enqueueMessage方法加入到队列中。
6、如何加入到messageQueue中,代码如下:
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
。。。
//1-----------
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;
//2------------
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
注释1处,全局变量mMessages一开始是为空的,并在注释2处对其赋值成最新的消息。然后当前msg的下一个元素指向mMessages(当取消息的时候就会将mMessages取出来),然后并把当前msg插入到链表中。此时消息等待被消息
7、取消息通过Looper.loop方法,代码如下:
public static void loop() {
//1------------拿到当前消息队列
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 (;;) {
//2-------------消息队列中取出一条消息
Message msg = queue.next(); // might block
try {
//3-----------------消息交给handler处理
msg.target.dispatchMessage(msg);
} finally {
}
}
}
注释2处就是从队列中取消息,注释3将消息交给当前当前消息的handler处理。这里的 msg.target在enqueueMessage()的时候就已经赋值。最终交给交给回调方法handlerMessage()处理。重点看注释2的next()方法,代码如下:
Message next() {
for (;;) {
//3-------------native层
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message prevMsg = null;
//1-------------------------将全局的消息赋值
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
} else {
//2---------------------返回消息
return msg;
}
} else {
}
}
}
}
首先会获取全局的消息对象,该对象的赋值在第6步已经完成,然后将该消息返回。这里使用死循环。为什么不会导致程序的ANR呢,其实这个方法除了返回message对象,另外就是在调用native层方法对资源进行释放。简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
二、手撸Handler核心代码
1、创建测试类HandlerTest.java代码如下:
public class HandlerTest {
@Test
public void main(){
//创建looper
Looper.prepare();
//接受消息
Handler handler = new Handler(){
@Override
public void handlerMessage(Message message) {
super.handlerMessage(message);
System.out.printf("收到消息:%s\n",message.obj.toString());
}
};
//发送消息
Message msg = new Message();
msg.obj = "123";
handler.sendMessage(msg);
//循环取消息
Looper.loop();
}
}
测试方法模拟消息发送和接受的过程。
2、创建Looper.class,代码如下:
public class Looper {
public static ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
public static MessageQueue sQueue;
public Looper() {
sQueue = new MessageQueue();
}
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
public static Looper myLooper(){
return sThreadLocal.get();
}
public static void loop() {
Looper looper = myLooper();
if (looper == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
while (true){
Message message = sQueue.next();
if(message != null)
message.target.dispatchMessage(message);
}
}
}
内部实例化一个threadLocal和维持MessageQueue对象的引用。并提供prepare()和loop()方法
3、创建Handler.class,代码如下:
public class Handler {
private Looper mLooper;
private MessageQueue mQueue;
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.sQueue;
}
public void dispatchMessage(Message message) {
handlerMessage(message);
}
public void handlerMessage(Message message){
}
public void sendMessage(Message msg) {
msg.target = this;
mQueue.enQueueMessage(msg);
}
}
内部维持Looper对象的引用,并通过looper对象拿到唯一的消息队列mQueue。提供发送消息方法sendMessage()和处理消息方法handlerMessage()。
4、创建消息Message.class。代码如下;
public class Message {
public int what;
public Handler target;
public Object obj;
@Override
public String toString() {
return "Message{" +
"what=" + what +
", target=" + target +
", obj=" + obj +
'}';
}
}
5、创建消息队列MessageQueue.class。代码如下:
public class MessageQueue {
private BlockingDeque<Message> mBlockingDeque = new LinkedBlockingDeque<>();
public Message next() {
try {
return mBlockingDeque.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public void enQueueMessage(Message msg) {
try {
mBlockingDeque.put(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
采用阻塞队列方式模拟消息的加入队列和取出队列的过程。提供next()和enQueueMessage()方法。
6、测试结果,截图如下:
收到消息,并且队列处于阻塞状态。
这里抛出一个经常遇到的面试题:消息机制中Looper.loop()死循环不会阻塞主线程,导致ANR吗?答案是不会阻塞主线程,因为调用loop()方法,会调用MessageQueue的next()方法,这个方法中会调用nativePollOnce()底层方法,当消息阻塞时,会释放cpu资源。一旦有消息需要处理,会通知它立马运转起来。所以android是消息驱动机制的,只有不断的消息循环才能唤醒cpu,真正导致ANR的原因是:其中有消息不能在规定时间内处理,导致其他消息无法处理,而不是死循环。