Android Handler消息机制,在Android中扮演了很重要的角色。通过Android Handler,能够实现延迟处理、子线程更新主线程界面等功能。(Android更新UI,一定需要在主线程中,这个规定的原因是,多个线程同时更新UI会造成UI界面更新混乱,不利于管理,因此,程序在初始化时,主线程中就初始化了UIHandler)。
Handler机制主要涉及到几个类:
- Handler
- Looper
- ThreadLocal
- MessageQueue
后面将从简单到复杂分析他们各自的功能,以及之间的关系。
ThreadLocal
ThreadLocal是一个线程相关的存储类。ThreadLocal的存储,获取数据:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
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);
}
ThreadLocal中常用的方法主要是这两个:
- set(T value)方法,传入一个泛型对象,首先获取当前线程的对象,然后values(currentThread),这个方法其实获取currentThread.loacalValues,也就是说每个Thread都有localValues对象。如果这个values为空,则初始化,否则直接put添加这个数据。我们查看Values这个类的put方法:
void put(ThreadLocal<?> key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
首先cleanup,这个方法会调用rehashed,如果数组容纳的空间不够,会进行空间扩展。 然后外层是循环,循环的第一个判断,如果找到原来设置的key,则直接修改原来key对应的值(保存的时候,是index保存键,index+1保存值); 循环的第二个判断,如果没有找到对应的key,并且firstTombstone=-1,则在最后面保存key和value; 第三个判断,如果k!=null并且firstTombstone=-1和k==TOMBSTONE,那么firstTomebstone=index,然后在下次循环中,将数据赋值到index这个位置(因为这个位置的数据是被删除的位置,被删除的数据会被赋值为TOMBSTONE)。
- get() 这个方法中,通过hash计算出对应的index,并返回对应的值。 不做过多说明。
总结一下ThreadLocal:
1. 每个Thread都有一个Values对象,这个对象用来存储数据。
1. ThreadLocal操作对应线程的数据,其实是操作在线程的Values中的数据。
1. put方法,会先检查数组容量,然后将数据保存在数据中,如果其中有废弃的数据,则会这个位置的数据会被覆盖。
1. get方法,在线程的values中,查找指定位置的值。这个位置通过ThreadLocal的hash和Values的mask值来计算。
2. ThreadLocal是线程级保存数据的。
MessageQueue
它保存了由Looper分发的一系列事件Message,MessageQueue主要用来操作Message消息的,它核心的方法主要是:enqueueMessage,next,quite。
- enqueueMessage
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 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;
}
分析一下上面方法的流程:
1. 首先判断这个Message的Handler是否为空以及消息是否已经在使用。
1. 然后同步方法中,先判断当前消息队列是否退出,如果退出了则抛出异常,并将这个消息回收掉。
2. 如果消息队列没有退出,则开始将消息添加到队列中:先将消息设置为正在使用中状态(makeInUse),如果当前头结点为空,则将这个消息Message作为头结点,头结点的名称依然还是mMessages,这时,重新将阻塞的线程激活
3. 如果头结点不为空,则先执行for循环,将p指向到队列的末尾结点的next结点,最后将Message添加末尾结点的next结点,而Message的下一个结点指向p这个空结点。这样就把新消息添加到了队列末尾。
示意图(将消息插入末尾):
- next
这个方法的作用是从MessageQueue中取出一条消息。下面是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;
}
...
- 外层是一个死循环,只有当这个方法获取到一个Message时或者MessQueue调用了quite方法,才会跳出循环返回。
- 中间开始是一个同步的方法体,避免多线程同时访问数据。
- if先判断是否存在无效的信息(target为空),如果target为空,开始循环找出后面的第一个异步消息。消息里面有普通消息,同步消息,异步消息。同步消息的插入postSyncBarrier这个方法,同步消息没有target,当代码运行到这里时,只会取出异步消息来执行。
- 如果msg不为空,先判断现在的时间是否大于消息的时间,如果大于,则取出头部的第一个消息(mMessage=msg.next)。这里有可能因为preMsg!=null(第三步说的情况),因此 会执行prevMsg.next = msg.next。最终都是取出一条消息返回。
下面用图来说明:
- quite
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
主要是两个判断
1. 是否运行退出循环,主线程中的Looper是不允许的。
2. 是否安全退出,非安全退出,则队列中所有的消息将会因为调用removeAllMessagesLocked()移除;安全退出只会删除时间大于当前的消息,已经在队列中可执行的消息不会删除。
Looper
Looper的主要作用是在当前线程中运行一个消息循环队列。sMainLooper是一个静态变量,在主线程调用prepareMainLooper时候就会被赋值。因此这个的获取就很简单,调用getMainLooper就ok了。
后面主要讲讲Looper的核心方法,按照Looper的使用方法顺序,prepare(),loop(),quite()方法说明。
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的初始化直接调用prepare静态方法,会先new一个Looper对象,然后保存在当前线程中。
loop
public static void loop() {
...
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);
...
}
}
上面是loop方法的核心代码,主要是通过死循环不断的调用queue.next()获取队列中的消息,并调用dispatchMessage将消息发送给Handler。 消息队列的next()方法当消息队列中没有消息后,会阻塞;当调用了quit方法后,next()返回null,然后loop的循环也会退出。总体来说,流程比较简单。
Handler
Handler是发送消息和处理消息的主要参与者。下面主要分析发送消息和处理消息的相关方法:
enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以发现,Handler里面的sendMessage或者是post相关方法,最终都会调用enqueueMessage方法,最后其实是MessageQueue的enqueueMessage方法,这就回到上面MessageQUeue分析的过程了。
dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先判断Message有没有设置callback,如果设置了直接调用callback处理,否则调用调用Handler的Callback或者是调用handleMessage方法处理。
以上就是Android中Handler的源码分析。接下来我们通过常用的使用方式结合源码再梳理一遍。
Looper.preprare()
Looper.looper
Handler handler = new Hander(looper)
hanlder.sendMessage
handleMessage
上面是整个用法的简单写法,Looper保存在prepare方法调用的所在线程中,Handle可以在其他线程中创建,只要能拿到looper对象,Handler的方法也可以在其他线程中调用。如果不指定looper对象,Handler绑定的就是当前线程的Looper对象。Looper对象的loop方法决定了Handler的handleMessage方法处理操作所在的线程。
到此为止,Handler的机制基本就结束了。