众所周知,在我们的应用程序中耗时操作需要放到子线程中去执行,当耗时操作执行完成后需要将结果通知给主线程(即UI线程)来更新UI;如果耗时操作放在主线程执行,则会造成主线程的阻塞,从而导致ANR问题。Handler就是针对于这一问题而提出的一套异步消息处理机制,用于线程间通信。Handler消息机制主要包括四部分:Looper、MessageQueue、Handler以及Message。
简介
-
Looper
我把它翻译为循环者,它循环的是什么呢?Looper内部维持着一个消息队列(MessageQueue),队列中存放的是一个个消息(Message)对象。Looper循环的就是不断从MessageQueue中取出Message对象; -
MessageQueue
消息队列,用来存放待处理的消息(Message); -
Message
消息,Message作为一个载体,用来携带线程间通信的信息,从而将子线程里的数据传递到主线程。 -
Handler
顾名思义为处理者,但它不仅仅是处理者还是发送者。它负责向MessageQueue中发送消息,然后等待Looper循环取出消息后进行处理。
源码解读四者之间的关系
-
Looper
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的构造方法中它创建了一个MessageQueue对象,并把它赋值给自己的成员变量mQueue;这就是上文中说到的Looper内部维持着一个消息队列。
细心的读者可能会发现Looper的构造方法是被private修饰的,那么我们如何去创建一个Looper对象呢?
/**
* Return the Looper object associated with the current thread.
* Returns null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper提供了一个静态方法myLooper()来返回Looper对象,但是这个返回值被@Nullable所修饰,标识返回的值可能是null。具体原因源码里的注释里已经写的很清楚了 “返回和当前线程关联的Looper对象;如果当前线程没有关联Looper则返回null”。
从代码也可以看出,返回的Looper对象是通过sThreadLocal.get()得到的,既然有get()那么自然而然就会有set(),如果没有set()就直接get()那么肯定得到的就是null了。那么接下来就来看看这个sThreadLocal;
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这里的注释也写到了“sThreadLocal.get()方法会返回null,除非你已经调用了prepare()方法”
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
/**
* 先调用sThreadLocal.get()方法
* 如果获取到的值不为空
* 即说明当前线程中已经存在Looper对象, 抛出异常
*/
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
prepare()方法中会先调用sThreadLocal.get(), 如果返回值不为空,说明当前线程已经关联了Looper对象,此时会抛出异常提示同时也保证了“一个线程中只可以创建一个Looper”;只有当当前线程还没有关联Looper对象的时候会去创建Looper,并且通过sThreadLocal的set()方法传入已创建的Looper对象,将二者关联起来。
我们现在已经知道如何去创建一个Looper对象(先通过prepare()方法在当前线程中创建出唯一的Looper对象,然后通过myLooper()方法来获取这个Looper对象),也知道在创建Looper的同时,Looper内部会创建一个MessageQueue并赋值给自己的成员变量mQueue。
上文说到Looper是一个循环者,循环的对象是MessageQueue,现在循环者和循环对象都有了,接下来就是循环了。
public static void loop() {
// 通过myLooper()获取Looper对象
final Looper me = myLooper();
// 获取到的Looper对象为空, 抛出异常提示“没有调用Looper.prepare()方法”
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取Looper对象中的MessageQueue实例
final MessageQueue queue = me.mQueue;
// 开始循环
for (;;) {
// 从MessageQueue中取出消息
Message msg = queue.next();
// 没有消息阻塞等待
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 将Message交给Message自身的target属性去dispatch
try {
msg.target.dispatchMessage(msg);
}
// 回收消息
msg.recycleUnchecked();
}
}
loop()方法很长,这里我做了简化,每行代码都已经做了注释,这里再简单总结一下:loop()方法通过一个for循环将MessageQueue中的Message取出,并交给Message自身的target属性去处理。这里先说一下,Message的target就是Handler,下面我们说Handler的时候会说到。
现在已经有Looper了,并且Looper也能够循环从MessageQueue中取出待处理的Message;现在就还有两个问题了:
- MessageQueue种的Message从哪里来?
- 取出来的Message该由谁来处理?
即从哪来,该往何处去的问题。下面就该轮到Handler上场了(Handler内心:不是说好我是主角的吗???)
-
Handler
我们一般在代码中使用Handler的时候就是直接new一个Handler出来,需要写handleMessage方法就重写一下,不需要就不写
val handler1 = Handler()
val handler2 = object : Handler(){
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
}
}
所以我们还是先从Handler的构造方法开始看
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
// myLooper()方法获取Looper对象,赋值给成员变量mLooper
mLooper = Looper.myLooper();
// Looper == null时,抛出异常提示没有调用prepare方法
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 获取Looper中的MessageQueue并赋值给自己的成员变量mQueue
mQueue = mLooper.mQueue;
// 将传递进来的两个参数赋值给对应的成员变量
mCallback = callback;
mAsynchronous = async;
}
我们平时创建Handler用的空参构造函数内部其实走的是Handler(Callback callback, boolean aync)构造函数,默认传的null 和false,这两个参数不是本文讨论的重点,这里不做赘述。
我们主要看这个构造函数里做了什么操作。代码里我都已经做了注释,简单总结一下:通过Looper的静态方法myLooper()获取Looper对象,将获取到的Looper对象和Looper对象的mQueue分别赋值给自己的Looper和mQueue,如果获取的Looper为空,抛出异常提示“没有调用Looper.prepare()"方法。
这里先提一个问题,“为什么我们在主线程中可以直接创建Handler的而不会抛异常?”
现在已经创建好了Handler对象,那么接下来就该sendMessage了,不发送消息自然就没有消息需要处理了是吧。
Handler发送消息的方法有很多,这里主要来看sendMessage()和sendEmptyMessage()
- sendMessage()
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
可以看到sendMessage()方法里会调用sendMessageDelayed()方法,在sendMessageDelayed()方法里又会调用sendMessageAtTime()方法。
- sendEmptyMessage()
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
sendEmptyMessage()方法内部调用了sendEmptyMessageDelayed()方法,sendEmptyMessageDelayed()方法内部先通过Message.obtain()得到一个Message对象,然后调用了sendMessageDelayed()方法;
通过上文的分析我们知道,sendMessageDelayed()方法调用的是sendMessageAtTime()。
所以其实不管我们使用的是什么方式来发送消息,最终都会走到sendMessageAtTime()方法**
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
...
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
sendMessageAtTime()方法里面将mQueue作为参数传递到了enqueueMessage()方法里,这个mQueue你应该还记得是从Looper中获取的。
- enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// Handler将自身赋值给了Message的target属性
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
到这里我们终于知道,为什么Message的target属性是Handler了,在Handler的enqueueMessage()方法中,Handler将自身赋值给Message的target,然后通过MessageQueue的enqueueMessage()方法将赋值后的Message插入到队列中;这样当Looper通过loop()循环出Message并交给Message的target来处理的时候,实际上就是交给Handler来处理了。所以接下来就是dispatchMessage()了
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这个方法很简单,就是一个处理消息的优先级问题,首先是msg.callbak, mCallback,最后是handleMessage()也就是我们经常重写的那个handleMessage()方法。这里简单说一下这两个callback:
- mCallback
这个不用说,大家应该还记得,我们在构造Handler的时候默认是无参构造,默认传的是null,所以这里消息默认不会被mCallback所处理,相反如果你构造的时候传值给了mCallback,那么你就不能在handleMessage中来处理了,因为根本就不会轮到它来处理了。 - msg.callback
这个大家可能会有点陌生,但其实我们经常会用到它;大家应该还记得Handler有一个post(runnable)方法
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
在post(runnable)方法里会调用getPostMessage(),在这里会将你传递的runnable赋值给Message的callback。从而能够在runnable中去处理。
好了到这里Handler消息机制就说的差不多了,基本流程就是这样。我在上文中提过一个问题“为什么我们在主线程中可以直接创建Handler而不会抛异常?”。下面我们就来看一下这个问题,其实也很简单。
-
为什么在主线程中可以直接创建Handler
因为在Android应用程序的入口main函数中,主线程Looper已经prepare()和loop()了
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
结语
以上就是本人所理解的Handler消息机制。本人水平有限,如有表述错误的地方恳请大家在评论区中留言指出,谢谢!
网友评论