Handler 相关
[TOC]
介绍
Android 主要是靠事件驱动的,而Handler在其中扮演重要的角色
Handler 是Android 中的一种消息的发送、处理机制.是Android中开发中常用的机制,在这简单说明下.
与之密切相关的有Looper,Message,MessageQueue.
整个机制的流程:
大致为如下
--> 发送消息-->添加到消息队列-->消息队列取出消息--->处理消息
整体的结构类似于生活中常见的传送带结构
可以带着以下几点问题
- 什么是消息,如何处理消息的;
- 如何发送消息,消息如何添加到消息队列中,
- 消息队列中如何取出消息,按什么样的规则
- 一个线程有几个Looper,怎么实现的不同线程间的通信.
- 异步消息是什么?
- 主线程中的Lopper中死循环,为何不会造成ANR
术语说明
消息(msg) : Message
Message
即Handler 发送与处理的对象。内部存在一个target 保存发送处理此msg的handler
节选部分关键源码
public int what;
@UnsupportedAppUsage
/*package*/ Handler target;
// sometimes we store linked lists of these things
@UnsupportedAppUsage
/*package*/ Message next;
private static Message sPool;
public long when;
从源码中我们可以很明显的看出,Message是一种链式的结构,next 指向下一个Message,
其中target 是Handler 类型,也正是我们处理消息的对象。
一种很典型的命令模式
Handler
首先看下Handler,发送处理消息都是通过handler进行的,所以可以看下handler的构造方法,
一般情况最好自己传入声明的Looper,Handler 与 传入的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;
}
Looper
Looper 负责管理MessageQueue,在内部通过不断地取出msg,分发给handler 处理。
Looper 构造方法中创建了MessageQueue 的成员变量,
- 我们使用Looper前 需要调用
Looper.prepare()
方法,且只允许prepare一次。 可以看下源码
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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中,且多次调用的话会直接抛出异常
即问题4的答案,通过ThradLocal 进行保证每个线程内只存在一个looper
- 通过Looper.loop() 函数来启动 MessageQueue,进行事件处理,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;
//进入死循环
for (;;) {
//1. 不断的取出数据,核心
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 可以设置一个logPrinter 进行 调试
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
//2. 将msg 分发给 msg的target(Handler)
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中取出msg,然后进行分发的
包装了一些监听分发的处理
核心点即是 Message msg = queue.next()
接着看 MessageQueue
MessageQueue
MessageQueue 是整个机制的核心点,那它是怎么创建的呢?
前面说到每个线程只会存在一个Looper,在Looper创建的时候会new一个MessageQueue对象,与当前线程绑定
//是否支持退出,默认是true支持、主线程不支持
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
逻辑流程
如上的几个类的大致说明,看下实际使用中的业务逻辑,在源码中的实现
1. 插入消息
前面查看handler的源码我们知道发送消息最后都调用到了MessageQueue的enqueueMessage
方法
下面为源码看下具体是怎么插入到队列中的
//Handler 发送消息,实际还是通过持有的 MessageQueue 进行插入逻辑
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
//MessageQueue
boolean enqueueMessage(Message msg, long when) {
//插入消息禁止无目标的,同步屏障不是通过此方法设置
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//正在退出的话,则不管此条消息
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;//是否需要唤醒
//1.头msg为空,或者要插入的msg时间戳小于队列头的.直接放在队列头
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 {
//2. 1 不满足的话,则很明显是要插入到队列中,
// 如果当前是同步屏障且该消息是异步消息则默认是需唤醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//遍历找出当前应该插入的位置,从前往后
for (;;) {
prev = p;
p = p.next;
//到达链尾或者找到了比该msg更后的
if (p == null || when < p.when) {
break;
}
//如果是插入到队列中的其他的异步msg的后面,则不需要唤醒,因为前面的唤醒了
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
//3.插入到 prev 和 p 之间
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;
}
PS: 同步屏障
频繁的提到了同步屏障这个东西,这个是什么呢,怎么插入?
整体讲同步屏障是一个特殊的Msg,设置之后会阻止读取同步的msg,只允许分发异步msg,
通过postSyncBarrier()
设置,removeSyncBarrier
移除,@hide方法,
此方法慎用,如果 同步屏障未移除那么就无法处理业务事件了,相当于无反应了,且不会ANR
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;//屏障当前的值,每次+1,移除时需要对应的token
//1.构建同步屏障的msg
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
//2.设置同步屏障的位置,prev 和 p之间
// 依据when 进行插入
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
//3.插入
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
为什么需要设置呢?
同步屏障最常的使用地点就在ViewRootimpl里,在scheduleTraversals
方法中设置,此方法熟悉的娃肯定知道是更新UI时会被调用到的.很简单的可以联想到奥 设置同步屏障是为了保证UI的更新
例如: 当前消息队列已经充满了很多msg,等待处理,此时 vsync信号到来需要更新界面,总不可能让UI更新的消息排队吧,那样界面就卡主了.
看下scheduleTraversals
的源码流程也很容易发现此点。Choreographer.postCallback
发送的是异步的msg
2. 取出消息
通过接下来查看关键的next
方法,调用代码查看之前的Looper.loop()
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; //下一次取消息的时间间隔
//1.que的死循环
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//2.native 方法,会阻塞,底层通过epoll进行阻塞,在用msg输入时唤醒,或者等待时间到达时
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//3.如果当前 message 是同步栅栏
// 遍历寻找下一个异步的 message,赋值给 msg
if (msg != null && msg.target == null) {
do {
//prevMsg 为 异步msg前的一个同步message
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//4.当前的msg不为空(将要被处理的msg)
if (msg != null) {
//4.1 当前 message 的时间,还未到,计算需要等待的时间
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);
//4.2 当前 message的 时间到了,返回此msg给looper分发
} else {
mBlocked = false;
//说明msg是异步消息,取出msg
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
//当前msg的下一个msg,mMessages
mMessages = msg.next;
}
//断链
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
//5.找不到msg,消息队列空了或者这个栅栏后,没有异步msg了
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;
}
//线程空闲时,交给IdleHandler去处理。
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;
}
}
从源码中 很容易的可以看到,next方法也是一个死循环方法,整体作用是不断地循环取出msg(交给Looper分发出去)
关键点1
取出msg的时候会通过native方法进行阻塞调用
nativePollOnce
通过Linux的底层 epoll,监听文件描述符的IO事件,msg插入时会通过nativeWake
也会操作文件的描述符
epoll
其中通过传入的参数进行不同的等待
MessageQueue阻塞nextPollTimeoutMillis毫秒的时间。
1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回
3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
关键点2
成员变量mMessages
指代队列中第一个msg(代表将要被处理的msg)
网友评论