Handler的消息还有异步的?怎个异步法?UI线程的Handler执行时不是统一在UI线程吗,怎么能异步?如果你有这样的疑问,那你需要看看源码咯。 Handler的异步消息不是按照执行时同步异步来说的,这个异步消息对于Message来说就是一个标记位,标记当前消息是一个特别的消息。 通过 什么是Handler的同步屏障机制?通常我们使用Handler发消息的时候,都是用的默认的构造方法生成Handler,然后用send方法来发送消息,其实这时候我们发送的都是同步消息,发出去之后就会在消息队列里面排队处理。 我们都知道,Android系统16ms会刷新一次屏幕,如果主线程的消息过多,在16ms之内没有执行完,必然会造成卡顿或者掉帧。那怎么才能不排队,没有延时的处理呢?这个时候就需要异步消息,在处理异步消息的时候,我们就需要同步屏障,让异步消息不用排队等候处理。可以理解为同步屏障是一过滤网,把同步消息队列拦住,先处理异步消息,等异步消息处理完了,这网就会取消,然后继续处理同步消息。 所以上文说的异步消息不受同步屏障约束,是说同步屏障约束的是普通同步消息! 怎么来使用这个同步屏障呢?在MessageQueue里面有postSyncBarrier方法: 其实很简单,就是创建了一个消息,但是值得注意的是,这个消息没有target,普通的消息的必须有target的。然后我们来看看怎么取出消息。 这里可以看到有一个很关键的判断,上面我们知道屏障消息的target为空,所以这里判断为true,while循环直到取出异步消息终止。接下来的处理就跟同步消息一样了,这里不赘述。 发送异步消息要搭配发送同步屏障一起使用才能生效。 发送同步屏障消息只能通过反射操作。 发送同步屏障消息后,异步消息处理完成,需要主动移除屏障,否则主线程会一直挂着! 在Android系统里面为了更快响应UI刷新在异步消息
/**
*如果是true的话标明该消息是个异步消息,不受同步屏障的约束
*/
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
/**
* 设置为异步消息,不受同步消息屏障约束
* <p>
*对于某些操作,比如view的刷新,会引入同步屏障消息在Looper的消息队列中,防止后续的消息一直分发直到某些情况导致view的刷新被卡住。在调用View#invalidate后就会发送messages通过同步屏障挂起,直到View的下一帧即将要Draw。同步屏障确保UI刷新在能够正常执行。
* </p><p>
*异步消息对于同步屏障来说是免疫的。这些通常是指一些中断操作、输入事件或者是其他处理事件的信号,此时需要挂起其他工作。
* </p><p>
* </p>
*标识异步消息顺序在分发时对于有序的同步消息来说是无用的。如果你的消息需要保持一定的顺序请谨慎使用
* @param async True if the message is asynchronous.
*
* @see #isAsynchronous()
*/
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
setAsynchronous()和isAsynchronous()这2个方法注释,可以大概了解到异步消息不受同步消息屏障的约束,对于有序的同步消息来说,异步消息是无序的。Handler同步屏障(SyncBarrier)
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
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++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
处理异步消息
//MessageQueue.java
Message 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());
}
省略代码
}
}
如何发送异步消息
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
public Handler(boolean async)
public Handler(@Nullable Callback callback, boolean async)
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)
如何应用
public void postSyncBarrier() {
Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
token = (int) method.invoke(Looper.getMainLooper().getQueue());
}
public void removeSyncBarrier() {
Method method = MessageQueue.class
. getDeclaredMethod("removeSyncBarrier", int.class);
method.invoke(Looper.getMainLooper().getQueue(), token);}
}
ViewRootImpl.scheduleTraversals也有应用:void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
支付宝扫码打赏
微信打赏