前言
这篇文章本来应该前几天就出炉的,不过笔者当时正看得起劲,头忽然昏的不行,心想难道是撸多了?咳咳..最后脑供血不足去医院检查了一下,做了脑部CT发现很健康,这就奇怪了。后来想想应该是当时门窗都关的太严实了,刚好外面装修的甲醛充满了房间..所以各位在学习的同时一定要锻炼身体啊(好像也没啥因果关系- -),不然什么都是浮云了….
进入正题,之前我们研究四大组件源码时,有一个H类大家印象一定很深刻(滑稽),每次AMS跨进程调用ApplicationThread的Binder方法时,都会调用H跟ActivityThread通信。这个H类就是我们今天的主角:Handler,通过他我们可以很方便的在线程间通信。相信大家对他再熟悉不过了,不过真的把他看透了吗??我们今天就把他扒光了看个清楚。
要了解Handler首先要知道ThreadLocal、Looper、MessageQueue这几个类,我们就一一道来。
二、ThreadLocal
ThreadLocal是一个线程内储存数据类,通过他可以在指定线程内存储数据。一般情况下我们很少见到他,下面我们通过一个栗子来认识认识他:
private void testThreadLocal(){
final ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();
mThreadLocal.set(1);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(mThreadLocal.get());
mThreadLocal.set(2);
}
}).start();
System.out.println(mThreadLocal.get());
}
会输出什么呢?按理应该是输出
1
2
对吧?答案当然是错误的啦,正确输出应该是
null
1
这就是线程存储的意思了,虽然是同一个ThreadLocal对象,但是在不同线程里他储存着不同的值,我们的Looper就是这样的存储在相应的线程里,也就是我们所熟知的:必须要在每个线程中调用Looper.prepare()后才能使用Handler。
首先来看看他的set方法
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
首先获取当前线程,然后用当前线程获取Values对象,这个对象其实是Thread的成员,同时他也是ThreadLocal的内部类,具体作用和map类似,只是算法不一样而已,具体算法大家可以参考ThreadLocal.Values这个类,这里不深入探讨。
最后把ThreadLocal本身作为key,传入的参数作为Value保存了起来。再看看get的实现呢:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
我们不管具体储存方式,总之就是通过当前线程获取Values这个”map”,然后取出刚刚存储进去的值。
是不是看起来很简单,其实就是通过Thread里的Values来储存数据,你在哪个线程里调用,获取到的就是那个线程的Values,当然get、set操作的也就不是同一个Values了,这就可以解释为什么我们在不同线程获取到的值不同了。
这里简单的了解了一下ThreadLocal的功能和实现,有助于我们理解Handler的实现方式。如果想更深入理解ThreadLocal,笔者这里有一篇推荐文章:Java并发编程:深入剖析ThreadLocal
三、MessageQueue
稍微熟悉一点Handler的童鞋应该知道,Handler大概工作过程就是发送消息,然后把消息储存到队列中,一个一个执行,没有消息的时候就等待。这个队列,就是MessageQueue。
虽然MessageQueue名字是一个队列,但是他其实并不是一个数组或者集合,底层实现就是一个链式的数据结构。他主要有两个方法:enqueueMessage(插入消息)、next(处理消息),我们先来看看如何插入:
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这就是一个将Message插入数据”链”末端的操作,每一个Message都有一个next,这个next就是下一条消息,我们的消息被插入到最后一个Message的next里了。就像这样
接下来看看如何处理消息
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
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 (false) Log.v("MessageQueue", "Returning message: " + msg);
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("MessageQueue", "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;
}
}
稍微有些长,但是其实就是一个无限循环地获取消息的操作,整个for循环里只有两处return(结束循环)的操作,第一个是获取到消息并返回,第二就是mquit为ture,也就是退出操作时。也就是说,当有消息发送过来时,就返回给调用方(其实就是Looper),不然就阻塞。
四、Looper
我们知道,在使用Handler之前,必须调用Looper.prepare,然后还需要调用Looper.loop来启动消息循环。这里面都做了些什么操作?先看prepare
public static void prepare() {
prepare(true);
}
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));
}
很简单,就是把Looper对象保存了一个在sThreadLocal里,也就是当前线程里,相当于给Thread绑定了一个Looper。
接着看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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
//...省略部分
}
}
1、取出刚刚保存的Looper,Looper里有一个MessageQueue
2、调用MessageQueue的next方法,刚刚我们分析过,如果没有消息,将阻塞,同时这里也会阻塞
3、如果有消息返回调用msg.target.dispatchMessage(msg),其内部就会调用我们平时重写的方法啦,这里先卖个关子,后面再看。
4、处理完成后继续回到第一步,无限循环下去。
这里就可以很好的解释Handler的跨线程通信了,因为Looper.loop在哪里调用,获取的就是那个线程的Looper,也就是那个线程的MessageQueue。什么?还不理解?OK,接下来看完我们今天的主角,相信你就知道了。
五、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;
}
嗯嗯,果然绑定了当前线程的Looper,也就意味着,无论你在哪里使用Handler,他的Looper都是创建他那个线程里的Looper,当然,消息也就会在那个线程里处理,因为loop方法是永远执行在那个线程里的,跨线程通信也就实现了。是不是豁然开朗?无论你是不是,反正我就当是了。
这里解开了我心中谜团,接下来该看看Handler是如何将消息传过来的。平时我们发消息有两种方式
handler.sendMessage(what)
handler.post(runnable)
查看源码,发现他们最终都走到了enqueueMessage方法,只是post方法给message设置了callback而已。我们先看看enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
就干了一件事,把消息插入队列中了,这样我们的Handler工作就完成了。
等等,刚刚留下的坑要填上,msg.target.dispatchMessage(msg)这个方法会调用我们实现的方法,现在来看看
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我们一句一句来
1、如果msg的callback不为空走handleCallback方法,这个callback刚刚一句说过了,是post方式才传入的,所以如果是post就走这里,启动runnable。
2、mCallback不为空就走mCallback.handleMessage(msg),这个mCallback是什么鬼?其实,Handler还有一种实现方式,大家看文章开头(看个毛啊),算了我再放出来一次:Handler的构造方法
public Handler(Callback callback, boolean async) {
//省略了....
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
所以,这个mCallback其实是在创建时传入的,如果你喜欢,你还可以这么写:
Handler handler =new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
用了这么久的Handler第一次知道他的实现方式还有第三种(可能是笔者太菜),侧面反映出源码的重要性。这个实现方式有一个返回值,如果为true,就不会再去调用handleMessage,反之还会继续调用,仔细想想,Handler这里又给我们多了很多操作空间哦。
3、handleMessage…不说了。
六、总结与扩展
1、使用ThreadLocal将Looper与线程绑定,Handler与Looper绑定,使Handler的dispatchMessage方法始终执行在指定线程里,从而达到调换线程的目的。
2、老生常谈:注意Handler内存泄漏。还要注意的是,由于在子线程开启Looper后会无限循环地去获取消息,所以并不建议在子线程开启,或者说,用完一定要记得回收,调用removeCallbacksAndMessages传入null既可删除所有消息。
Looper的退出:Looper.quit会结束循环接收消息,Looper.quitSafely会处理完消息后自动退出。
3、记得我刚接触Android时,面试官问我:Handler的handleMessage和post方式有什么不同,我当时确实不知,后来面试官告诉我,一个是异步,一个是同步..我当时信以为真。现在仔细想想,同步?WTF?同步你用Handler?
好的,这里我也挖同样的坑,看完源码后大家可以仔细想想,Handler的handleMessage和post方式有什么不同??(滑稽)