结论
从API28开始,Handler
类增加了静态方法createAsyn
。主要作用是使所有通过这个Handler发送的Message,都会被设置为FLAG_ASYNCHRONOUS
异步消息(默认是同步消息),在搭配消息屏障使用的情况下,会被优先调用。
原理
#Handler.java
@NonNull
public static Handler createAsync(@NonNull Looper looper) {
if (looper == null) throw new NullPointerException("looper must not be null");
return new Handler(looper, null, true);
}
@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
if (looper == null) throw new NullPointerException("looper must not be null");
if (callback == null) throw new NullPointerException("callback must not be null");
return new Handler(looper, callback, true);
}
其中调用了
#Handler.java
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages. Also set whether the handler
* should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by conditions such as display vsync.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到限制了不能直接调用这个Hanlder
构造方法设置mAsynchronous
属性。mAsynchronous
在enqueueMessage的
时候将会设置到对应的Message
。
那么mAsynchronous
的作用是什么呢?消息循环时通过MessageQueue#next
获取下一个消息的。
MessageQueue.java
Message next() {
...
//msg.target是对应的Hanlder。这里为null说明是消息屏障,通过MessageQueue#postSyncBarrier方法添加的。
//可以看到,一旦添加了消息屏障,那么后面的同步消息将会全部被过滤。
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
...
}
这个消息屏障是什么时候添加的呢?经过源码翻阅是scheduleTraversals
的时候会添加一个消息屏障。unscheduleTraversals会移除消息屏障。安卓在发送UI更新的消息时,都会通过Messaged#setAsynchronous
设置为异步消息。安卓系统这里的设计是为了保证UI绘制相关的操作能够优先执行。
#ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
#MessageQueue.java
/**
* Posts a synchronization barrier to the Looper's message queue.
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier.
* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
* and continue to be processed as usual.
*
* This call must be always matched by a call to {@link #removeSyncBarrier} with
* the same token to ensure that the message queue resumes normal operation.
* Otherwise the application will probably hang!
*
* @return A token that uniquely identifies the barrier. This token must be
* passed to {@link #removeSyncBarrier} to release the barrier.
*
* @hide
*/
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
注意
这个接口创建的Handler
,假如是在Looper#getMainLooper
上。由于所有发送的消息默认是异步消息,会在UI绘制时优先执行,应该确认时必须优先执行的操作才使用此Handler
。