Handler
1. Handler 是什么?
Handler 是 android 中消息处理机制。一个Handler 会对应一个 Thread 和 该 Thread 绑定的 MessageQueue,Handler 会将 Message 发送到 MessageQueue 中,通过 Looper 轮训器的方式从消息队列中轮训消息,每次轮训到的消息都交由 Handler 去处理这个消息。
2. Handler 的作用是什么?
可以方便地将任务切换到 Handler 所在的线程中去执行。若是在子线程中进行耗时操作完成后需要更新 UI, 那么就可以利用 Handler 消息机制的特性对主线程UI进行更新。前提就是 Handler 是在主线程创建。
3. Message消息
3.1 什么是 Message ?
Message 是 Handler 发送消息的载体,它通过 Handler 发送出去然后 Looper 会在指定的 MessageQueue 去取出这个 Message,最后再将该消息交给指定的 Handler 去处理。
3.2 Message工作原理
每一个Message内部都有一个next属性指向下一个 Message,这样就构成了一个链表结构,而 sPool 是一个持有 Message 链表头部的引用,是一个 Message 类型的变量。每次通过obtain 方式获取到 Message 之后 sPoolSize--,这种方式获取 Message 好处在于重复利用这些消息,节约内存开销。这里需要注意的是在 5.2小节中,loop()的最后一段代码:msg.recycleUnchecked();它是负责对这些消息进行回收操作的。
3.3 创建消息方式一
//默认构造
public Message() {
}
//创建Message对象
Message msg = new Message();
3.4 创建消息方式二
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//sPool持有Message 池中的链表头的Message
Message m = sPool;
sPool = m.next;//指向到下一个Message
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
//创建Message对象
Message msg = Message.obtain();
4. 消息队列 MessageQueue 的工作原理
消息队列适用于管理消息的,主要就是消息的插入和移除操作。内部的数据结构采用的是链表结构,链表的特点在于添加和移除比较方便。通过 Hanadler 发送的消息最终会调用 MessageQueue 中 boolean enqueueMessage(Message msg, long when) 将消息插入。
4.1 boolean enqueueMessage(Message msg, long when) 插入一条消息
往链表中添加一条数据,若当前链表没有数据则该 Message 做为 head,若是链表有数据则比对所有的消息的 when 值,将Message 插入到指定的位置。
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
//标记 msg 被使用中
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
4.2 next()方法取出消息队列中的消息
MessageQueue 中的 next() 方法是 Looper.loop() 中调用的。next() 是个死循环方法,直到消息队列中有消息,返回该消息结束循环。这里为什么需要使用死循环呢?因为消息队列中即便有消息,但是由于每一个 Message 有一个 when 属性,只有达到的 Message 的 delay 时间才会去执行当前消息,所以得通过死循环的方式知道该消息能够去执行才取出这个消息。
Message next() {
...
for (;;) {//死循环 轮询
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message. 获取到一个消息 直接返回,然后将消息从消息队列中移除
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
5. Looper 的工作原理
中文名叫轮训器,Looper 开启轮训之后,这个过程是一个死循环的过程,当消息队列中有消息时,轮训器会取出这个消息交给 Handler 去处理。在 Handler 创建时,会默认使用当前线程的 Looper 作为轮训器,那么它是如何去获取当前线程的 Looper对象呢?这里涉及到另外一个知识点,那就是 ThreadLocal 概念,它不是一个线程,它可以在不同线程中互不干扰的获取或者存储数据,可以通过 ThreadLocal 获取不同线程中对应的 Looper,但是线程默认情况是没有 Looper 的,需要使用Handler 就必须为当前线程创建一个 Looper 对象。而在主线程创建 Handler 的时候会默认使用主线程的 Looper,因为在 ThreadActivity中 main 方法已经创建好了 Looper 对象,所以在主线程使用 Handler 是不需要手动创建 Looper 的。
5.1 准备 Looper 对象
//准备一个 Looper 对象。
Looper.prepare();
//源码
private Looper(boolean quitAllowed) {
//创建 Looper 的同时,也会创建一个 MessageQueue 对象
//每一个 Looper 都会对应一个 MessageQueue 队列
mQueue = new MessageQueue(quitAllowed);
//每一个 Looper 都会对应的 一个线程
mThread = Thread.currentThread();
}
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));
}
5.2 开启轮训操作
这个方法是阻塞式的,死循环的从 Looper 绑定的队列中去获取消息(queue.next()),直到消息队列停止。当获取到消息之后,直接将获取到消息分发出去,具体分发过程待会再分析。
//开始循环
Looper.loop();
//源码
public static void loop() {
//判断是否已经创建了 Looper
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;
}
...
//将轮训到的消息进行分发,将任务切换到 Handler 所在的线程中去执行。
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();//回收这个msg
}
}
5.3 MainLooper 的创建。
因为在 ActivityThread#main 方法执行了以上两段代码,这就表示应用启动时就将主线程的 Looper 创建完毕了。所以在主线程中直接创建Handler,不用指定 Looper 即可使用。
public static void main(String[] args) {
Looper.prepareMainLooper();
...
Looper.loop();
}
public static void prepareMainLooper() {
prepare(false);//创建一个 Looper,当前的 Looper 是在主线程中调用的
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//在主线程的 Looper 赋值。
}
}
5.4 如何在子线程使用 Looper?
若是要在非主线程中去使用 Looper 就必须手动地去创建 Looper 对象。而创建 Looper 的方式是通过 Looper.prepare()实现, 底层调用的是 Looper.prepare(boolean) 创建一个 Looper 对象,在方法中判断 ThreadLocal 中是否保存了当前线程的 Looper 对象,没有的话,则创建一个 Looper 对象并且保存到 ThreadLocal 中的,具体的 ThreadLocal 的知识点参考:
http://www.jianshu.com/p/aef305d37152。 创建的同时会为 Looper 创建一个 MessageQueue 消息队列。该方法的作用只是创建一个 Looper 对象,并且保存起来,此时轮训器还没有开启循环操作。通过 Looper.loop() 开启消息轮训操作。
new Thread(){
public void run() {
Looper.prepare();
Handler handler = new Handler();//handler 使用的就是上面创建的 Looper
Looper.loop();
}
}.start();
5.5 停止消息轮训操作
Looper 中提供了两个方法去停止轮训操作,quit()的方法注释中可以看出该方法是unsafe的,因为quit()被调用之后,那么其他没执行完的消息就没有办法被执行,而 quitSafe() 的注释表示该方法是安全的,它会等到所有的消息执行完毕之后采取停止轮训操作。
/**
* Quits the looper.
*
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
*
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
* Quits the looper safely.
*
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
*
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
*
*/
public void quitSafely() {
mQueue.quit(true);
}
6. Handler 的内部实现原理
Handler 的职责就是负责往消息队列中发送和处理消息的。对于发送消息而言, Handler 中有一系列的 postXxx 和 sendXxx 方法。对于处理消息 Hadnler 是通过 handlerMessage 去处理的。
6.1 通过 sendMessage 的方式往 MessageQueue 插入消息
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//looper绑定的消息队列
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);
}
//sendMessageDelayed 最终会调用 enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//指定message的target为当前handler对象
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//交给 MessageQueue去处理
}
6.2 通过 post 的方式往 MessageQueue 插入消息
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//将一个Runnable转化为一个 Message 对象
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;//保存到callback属性的,之后分发时需要拿其做判断使用
return m;
}
不管是 post ,postDelay 还是sendMessage,sendMessageDelayed的方式去发送消息,底层都是调用 sendMessageDelayed 去实现。从 getPostMessage 可以知道其实发送一个 Runnable 对象实际上也是将其转化为一个 Message 对象去发送的。sendMessageDelayed 最终会调用 enqueueMessage方法,在这个方法 msg 做一些赋值操作,例如给 msg 绑定一个target,就是说当前这个需要发送的消息它有由这个 Handler 发出的,设置 target 的原因就是方便待会 MessageQueue 知道具体分发给哪个一个Hadnler 去处理这个消息。而代码最终会走到queue.enqueueMessage(msg, uptimeMillis);这个queue是在哪里赋值的呢?代码回到Handler的构造中可以看出,其实 queue 就是 Looper 中绑定的 MessageQueue 对象,在 Handler 中也保存了一份引用。
6.3 处理消息
消息是通过 轮训器轮训出来的,也就是通过 loop 中去不断的调用 messagequeue中的next 方法获取一条 Message 消息,代码回到4.2小节中,获取到的 Message,然后调用 msg.target.dispatchMessage(msg); 将轮训到的消息进行分发,将任务切换到 Handler 所在的线程中去执行。这里的msg.target 就是发送消息的 Handler 对象。
6.3.1 handler 是如何通过 dispatchMessage 去处理不同的消息的?
判断 msg.callback 是否为 null,这个 callback 刚才在 post 方式发送一个 Runnable 对象时,通过 getPostMessage(Runnable) 转化时,将 Runnable 作为 Message 的 callback 属性。也就是说若是 msg.callback 不会null,表示这个消息就是通过 post 方式实现的。若为null,分为两种方式,一是通过通过具有 Callback 参数来创建的 Handler,那么就会会调用 Callback#handleMessage(),二以默认的方式来创建的Handler,因此会调用 Handler#handleMessage()方法。具体哪种方式处理消息,用户只需要去覆写对应的handleMessahe 即可。好了,这就是我对 Handler 消息处理机制的理解。
//根据不同的处理方式分发这个消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//第一种方式:post 方式
handleCallback(msg);
} else {
if (mCallback != null) { //第二种方式:通过具有 Callback 参数来创建的 Handler
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//第三种方式:创建Handler时没有指定 Callback 属性。
}
}
//Handler 内部的一个接口
public interface Callback {
public boolean handleMessage(Message msg);
}
//默认的构造,没有给callback赋值
public Handler() {
this(null, false);
}
//具有 Callback 的构造来创建 Handler
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}