1. 什么是Handler
什么是Handler机制,是Android中系统提供的一套线程间通信机制,大部分情况下用于子线程和UI线程通信。
2. Android中需要线程通信的背景
Android的主线程是不允许做耗时操作的,如果在UI线程进行长时间的耗时操作,界面会卡住。卡顿的时间过长,就会ANR。
然而我们有些操作就是需要一些时间才能完成,而且我们后续的操作就是需要依赖这个结果,比如网络请求,复杂的运算等。对于这些不确定时长的操作(有可能消耗很长的时间),我们需要放到其他线程中去。
那么随之而来就有一个问题了,我在这个工作线程完成耗时操作,这个时候主线程怎么能感知到呢?这里就需要用到跨线程的通信了。
3. java中线程间通信常用手段
我们知道,在java中,最常用的线程间通讯的方法就是共享变量的方式。
同一个变量或者说对象在内存中可以被多个线程访问到,进行各种操作。然后通过synchronized
或者wait
、notify
的等方式来保证不同线程对同一对象进行操作,而不会产生线程安全问题。
4. Android中使用Java线程间通信的基础
在Android的Handler机制中,MessageQueue
就是这样一个对象,它维护的消息队列可供多个线程访问,修改。
顾名思义,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(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
// 把当前消息标记为正在使用
msg.markInUse();
// 设置消息的处理时间
msg.when = when;
// 获取当前所需要处理的Message对象
Message p = mMessages;
// 是否需要wake
boolean needWake;
// 当前消息为空,入队时间为0,或者入队时间小于当前消息的处理时间
if (p == null || when == 0 || when < p.when) {
// 说明需要入队的消息是在队列中需要第一时间处理的,那么放入对象的最前面
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 这种情况是需要插入到队列中间或者末尾的情况
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 根据消息需要处理时间when的大小,排好队列,插入到适当位置
for (;;) {
// 记录遍历节点的前一节点
prev = p;
// 把p指向p的下一个节点
p = p.next;
// 如果p已经是队尾节点 或者 该消息的处理时间在p的处理时间之前
if (p == null || when < p.when) {
// 那么去插入当这个位置
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 插入消息节点
msg.next = p;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
复制代码
Message next() {
// ...
for (;;) {
// ...
// 同步代码块, 查找下一个需要被处理的消息,返回回去
synchronized (this) {
// 获取当前时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 不断的查询下一个需要被处理的消息
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 {
// 找到需要现在处理的消息 msg
mBlocked = false;
// 把队列的标志从当前消息节点移到它的next节点
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;
}
// ...
}
// ...
}
}
复制代码
5. 了解Message
在源码中,我们可以看到,消息队列是对Message数据进行操作,我们了解一下Message
Message
是一个实现了Parcelable
的类,承载着信息在不同的线程中传递。对于Message中的what
,arg1
,arg2
,data
这些承载具体数据的载体,可以去源码中查阅。
这两个方法就是消息队列的入队和出队操作。有了MessageQueue,我们就有了可以支持多线程访问修改消息队列的基础了。那么这个队列又是被哪些类使用了?
在Message中还有个字段是target
,类型是Handler,指定此消息是归属在哪个Handler的处理范围内。那我们看看Handler是如果处理Message的。
6. Handler如何将Message放到MessageQueue中
Handler是承担着处理和发送Message的重担。
我们平常使用Handler的时候,通过Handler的实例对象调用sendMessage()
、sendMessageDelayed
、post(Runnable)
之类的,最终都是将一个Message的实例对象通过enqueueMessage()
方法放入到MessageQueue中。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
最终是通过MessageQueue的enqueueMessage()
方法将Message放入到MessageQueue中去,那这个queue
对象是哪里来的呢,我们全局搜索一下,可以再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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
复制代码
7.Handler与Looper的关联
可以看到,Handler中的MessageQueue是拿的Looper中的MessageQueue,那么Looper中的MessageQueue又是怎么来的呢,翻到Looper的源码中一看,在构造方法中有new出MessageQueue对象。
但是我们仔细一看,构造方法是private修饰的呢,那么Looper啥时候初始化呢,看看去,就在prepare()
方法中调用。
这里用到了ThreadLocal类,ThreadLocal是一个可以创建线程局部变量的类,通过使用ThreadLocal创建的变量只能被该线程访问到,其他线程是无法访问的。
我们可以把sThreadLocal
看成一个map,存放的是以Thread为key,以Looper为value的数据。我们可以以线程为key去获取是否有初始化过Looper。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
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));
}
复制代码
所以说,Looper.prepare()
就是用来初始化调用线程
的Looper。并且确保每个线程只能初始化一个Looper对象。
8. Looper如何处理MessageQueue
那么对于Looper是如何进行对Message的处理操作呢,我们可以再Looper的loop()
方法中找到答案
public static void loop() {
// 获取当前线程的Looper
final Looper me = myLooper();
// 如果Looper为空,说明Looper没有在该线程初始化
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 拿到Looper中的MessageQueue
final MessageQueue queue = me.mQueue;
// ...
// 不断从MessageQueue去拿消息
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// ...
try {
// 如果有需要处理的消息,则通过消息对应的Handler来处理
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// ...
msg.recycleUnchecked();
}
}
复制代码
在loop()
方法中,获取到当前线程的Looper对象,从Looper对象中拿到MessageQueue,然后不断去轮询队列,有消息就通过Handler的dispatchMessage()
调用handleMessage()
,然后就回到了我们需要处理消息的地方了。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
9. 总结
所以整个流程如下过程:
- 在线程启动的时候,会通过
Looper.prepare()
方法初始化该线程的Looper对象,以及Looper对象中的MessageQueue
。 - 然后在Handler在初始化的时候,就与该线程的Looper关联上了,然后Handler在其他线程通过
sendMessage()
将Message
放入到关联Looper的MessageQueue
中,这一系列操作都是与sendMessage()
处于同一线程。 - 与此同时,Looper所在的线程中,Looper是一直在
loop()
方法中轮询着MessageQueue的,别的线程在放入消息之后,Looper就会拿出消息来交由Handler处理,这一系列操作发生在Looper所在线程。