1.1 Handler概述
在Android开发中,经常会在子线程中进行一些操作,当操作完毕后会通过handler发送一些数据给主线程,通知主线程做相应的操作。
探索其背后的原理:子线程、handler 、主线程,其实构成了线程模型中的经典问题 生产者-消费者 模型。
生产者-消费者模型:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加数据,消费者从存储空间中取走数据。
- 保证数据生产消费的顺序(通过MessageQueue,先进先出)。
- 不管是生产者(子线程)还是消费者(主线程)都只依赖缓冲区(handler),生产者消费者之间不会相互持有,使它们之间没有任何耦合。
Handler:发送和接收消息。
Looper:用于轮询消息队列,一个线程只能有一个Looper。
Message:消息实体。
MessageQueue:消息队列用于存储消息和管理消息。
1.2 同步屏障
Handler的消息,可以分为三种:
- 同步消息:异步flag为false,一般构造的msg默认为同步消息。
- 同步屏障消息:MessageQueue.postSyncBarrier()会构建一个msg.target==null的同步屏障消息,进而开启同步屏障。
- 异步消息:异步flag为true,若是开启了同步屏障,MessageQueue.next()中会遍历查找异步消息进而优先执行。
2.1 Handler全解
图解:
- Handler通过sendMessage()发送Message到MessageQueue队列;
- Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理;
- 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。
- 将Message加入MessageQueue时,队列如果处于阻塞状态则会唤醒loop线程;如果MessageQueue中没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性的工作。
消息分发的优先级:
- Message的回调方法:
message.callback.run()
,优先级最高; - Handler的回调方法:
Handler.mCallback.handleMessage(msg)
,优先级仅次于1; - Handler的默认方法:
Handler.handleMessage(msg)
,优先级最低。
消息缓存:
为了提供效率,Message提供了一个大小为50的对象池,减少对象不断创建与销毁的过程。
2.2 同步屏障
Android 系统中的 UI 更新相关的消息即为异步消息,需要优先处理。比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了ViewRootImpl#scheduleTraversals()
,如下:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 开启同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 发送异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Choreographer#postCallback()-->postCallbackDelayedInternal
,如下:
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
// 设置为异步消息并发送
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
这里就开启了同步屏障,并发送异步消息,由于 UI 更新相关的消息是优先级最高的,这样系统就会优先处理这些异步消息。
最后,当要移除同步屏障的时候需要调用ViewRootImpl#unscheduleTraversals()
。
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
同步屏障的设置可以方便地处理那些优先级较高的异步消息。当我们调用Handler.getLooper().getQueue().postSyncBarrier()
并设置消息的setAsynchronous(true)
时,target 即为 null ,也就开启了同步屏障。当在消息轮询器 Looper 在loop()
中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。
3.1 Handler
3.1.1 new Handler()
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
// 匿名类、内部类或本地类都必须声明为static,否则会警告可能出现内存泄漏
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());
}
}
// 持有Looper的引用
// 是从所在线程的ThreadLocal中获取,不是创建Looper实例
mLooper = Looper.myLooper();
// 子线程不调用Looper.prepare(),直接实例化Handler抛出该异常的原因
// 主线程不需要,是因为应用启动的时候系统已帮我们启动了主线程的Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 持有MessageQueue的引用
mQueue = mLooper.mQueue;
// 回调方法
mCallback = callback;
// 是否将要发送的msg设置为异步消息的flag
mAsynchronous = async;
}
对于Handler的无参构造方法,默认采用当前线程TLS(ThreadLocal.ThreadLocalMap)中的Looper对象,并且callback回调方法为null,且消息为同步处理方式。只要执行的Looper.prepare()方法,那么便可以获取有效的Looper对象。
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler类在构造方法中,可指定Looper,Callback回调方法以及消息的处理方式(同步或异步),对于无参的handler,默认是当前线程的Looper。
3.1.2 sendMessage()
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
// 必须要有MessageQueue即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);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 将Handler与msg绑定
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
// 如果Handler的异步消息flag位true,则将该消息设置为异步消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 最后将消息交给MessageQueue处理
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler.sendEmptyMessage()
等系列方法最终调用MessageQueue.enqueueMessage(msg, uptimeMillis)
,将消息添加到消息队列中,其中uptimeMillis为系统当前的运行时间,不包括休眠时间。
3.1.3 post()
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
3.1.4 dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
// 当msg有回调方法时,回调msg.callback.run
handleCallback(msg);
} else {
// 当Handler存在Callback成员变量时,回调方法mCallback.handlerMessage()
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 回调自身的回调方法handleMessage()
handleMessage(msg);
}
}
分发消息流程:
- 当
Message
的回调方法不为空时,则回调方法msg.callback.run()
,其中callBack数据类型为Runnable,否则进入步骤2; - 当
Handler
的mCallback
成员变量不为空时,则回调方法mCallback.handleMessage(msg)
,否则进入步骤3; - 调用
Handler
自身的回调方法handleMessage()
,该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。
对于很多情况下,消息分发后的处理方法是第3种情况,即Handler.handleMessage(),一般地往往通过覆写该方法从而实现自己的业务逻辑。
3.1.5 obtainMessage()
public final Message obtainMessage()
{
return Message.obtain(this);
}
Handler.obtainMessage()
方法,最终调用Message.obtainMessage(this)
,其中this为当前的Handler对象。
3.1.6 removeMessage()
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
Handler
是消息机制中非常重要的辅助类,更多的实现都是MessageQueue
, Message
中的方法,Handler的目的是为了更加方便的使用消息机制。
3.2 Looper
3.2.1 new Looper()
/**
*
* @param quitAllowed looper是否允许退出
*/
private Looper(boolean quitAllowed) {
// 构造函数中实例化MessageQueue与之对应
mQueue = new MessageQueue(quitAllowed);
// 记录当前线程
mThread = Thread.currentThread();
}
3.2.2 prepare()
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
// 一条线程只能拥有一个looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 将Looper存入当前线程的threadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
public static void prepareMainLooper() {
// 设置不允许退出的Looper
prepare(false);
synchronized (Looper.class) {
// 将当前的Looper保存为主Looper,每个线程只允许执行一次。
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
3.2.3 loop()
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 获取TLS存储的Looper对象
final Looper me = myLooper();
// 子线程需要构造一个looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
// 死循环尝试从MessageQueue中获取msg
for (;;) {
// 可能会阻塞
Message msg = queue.next();
if (msg == null) {
// 没有消息,则退出循环
return;
}
...
try {
// 回调消息所绑定的Handler的dispatchMessage()
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
// 回收消息实例
msg.recycleUnchecked();
}
}
loop()进入循环模式,不断重复下面的操作,直到没有消息时退出循环。
- 读取MessageQueue的下一条Message;
- 把Message分发给相应的target;
- 再把分发后的Message回收到消息池,以便重复利用。
3.2.4 quit()
public void quit() {
// 消息移除
mQueue.quit(false);
}
public void quitSafely() {
// 安全地消息移除
mQueue.quit(true);
}
Looper.quit()方法的实现最终调用的是MessageQueue.quit()方法。
3.2.5 myLooper()
public static @Nullable Looper myLooper() {
// 获取TLS存储的Looper对象
return sThreadLocal.get();
}
3.3 MessageQueue
MessageQueue是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理,其中MessageQueue类中涉及的native方法如下:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
3.3.1 new MessageQueue()
MessageQueue(boolean quitAllowed) {
// 是否允许退出消息队列的flag
// Looper实例化时会将flage传入
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
3.3.2 enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
// msg必须要绑定一个Handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 采用锁同步机制,为了保证消息队列一次只会处理一个msg
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
// 消息机制已退出,则释放该实例并return
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;
// 排序
// 当前没有消息 或者 when==0 或者 when小于当前message的when时
// 置于链表头结点
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 {
// 插入队列中间。通常我们不必唤醒事件队列,除非队列头部有屏障,并且消息是队列中最早的异步消息。
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) {
// 唤醒 nativePollOnece 的沉睡
// 涉及底层的epoll机制
nativeWake(mPtr);
}
}
return true;
}
MessageQueue
是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
3.3.3 next()
Message next() {
final long ptr = mPtr;
// 当消息循环已经退出,则直接返回
if (ptr == 0) {
return null;
}
// 循环迭代的首次为-1
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
// 采用锁同步机制,为了保证消息队列一次只会处理一个msg
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) {
// 则往后遍历查找异步消息进而执行
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 {
// 获取一条消息,并返回
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 {
// 没有消息
nextPollTimeoutMillis = -1;
}
// 处理完所有待处理的消息后处理退出消息
if (mQuitting) {
dispose();
return null;
}
// IdleHandler,启动优化的方案之一
// 如果第一次空闲,则获取要运行的空闲器的数量。空闲句柄仅在队列为空或队列中的第一条消息(可能是屏障)将在未来处理时运行。
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);
}
// 运行空闲处理程序。我们只在第一次迭代期间到达这个代码块。
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);
}
}
}
// 将空闲处理程序计数重置为 0,这样我们就不会再次运行它们。
pendingIdleHandlerCount = 0;
// 在调用空闲处理程序时,可能已经传递了一条新消息,因此无需等待即可返回并再次查找未决消息。
nextPollTimeoutMillis = 0;
}
}
nativePollOnce
是阻塞操作,其中nextPollTimeoutMillis
代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。
当处于空闲时,往往会执行IdleHandler
中的方法。当nativePollOnce()返回后,next()从mMessages
中提取一个消息。