Handler分析
写本篇文章的原因也是为了知识的记录 在Android开发中,通常会在子线程做某些事然后通知主线程,这时候就会用到handler机制。如写Android听过的第一句话就是子线程不能更新UI,这时候就可以使用handler机制来通知主线程来进行ui更新。
Handler相关的类
Handler: 主要负责发送消息、接收消息 Looper: 轮训消息队列,每个线程只能有一个Looper Message: 消息实体 MessageQueue: 消息队列,用于存储消息、管理消息 单链表结构
Handler 基本使用
首先要 Looper.prepare()然后在调用Looper.loop()之后就是我们熟悉的创建Handler 用来发送、接受消息
public Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
mHandler.sendEmptyMessage(0);
但是我们发现在使用的时候并没有进行 Looper.prepare() Looper.loop()操作,是因为他们是在ActivityThread.java中 main() 中进行的初始化
public static void main(String[] args) {
........省略部分代码
Looper.prepareMainLooper();
........省略部分代码
Looper.loop();
}
Looper
看下Looper.prepareMainLooper()
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
再看下 prepare()做了什么
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了,再看下初始化Looper时干了什么
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
能让整个Handler机制跑起来的方法Looper.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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
创建Handler
最简单调用
public Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
最总会走到有参构造方法中
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
看下Looper.myLooper()
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Handler可以指定looper构造函数
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler发送消息
Handler发送消息sendMessage(Message msg) sendMessage通过一系列的方法重载sendMessageDelayed,sendMessageAtTime,enqueueMessage继续调动messageQueue中的enqueueMessage方法将msg放进messageQueue中通过looper取出交给Handler的dispatchMessage进行处理
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
创建Message
可以直接 new Message,但是有更好的创建方法Message.obtain(),因为这个方法可以检查是否有复用的message用于复用避免过多创建、销毁message达到优化内存,性能的效果。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
Message与Handler绑定
可以通过Message.obtain(Handler h)绑定
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
但是我们平时并没有绑定Handler因为是在Handler发送message时候最总会调用enquueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue
MessageQueue实在创建Looper时候创建的,通过前面的分析每个线程只能有一个looper,每个looper里只有一个MessageQueue,我看再看一下MessageQueue,这个类相当于一个仓库,我们如何能管理好这个仓库就是一个关键点了。enqueueMessage(相当于把消息放入库中),next(相当于把消息取出来)所以这两个方法是负责线程安全的主要挡口。
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) {
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;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
next方法
Message next() {
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
}
}
synchronized锁是一个内置锁,这个锁,说明的是对所有调用同一个MessageQueue对象的线程来说,他们都是互斥的,然而,在我们的Handler里面,一个线程是对应着一个唯一的Looper对象,而Looper中又只有一个唯一的MessageQueue(这个在上文中也有介绍)。所以,我们主线程就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候,主线程一次都只会处理一个消息,其他的都需要等待,那么这个时候消息队列就不会出现混乱。
消息机制之同步屏障
同步屏障的概念,在Android开发中非常容易被忽略,因为在平时太少见了,很容易被忽略。 线程的消息都是放在同一个MessageQueue中的,取消息时候是互斥的。而且只能从头部取消息,而添加的消息也是按照执行的先后顺序进行的排序,那么问题来了,同一时间范围内的消息,如果它需要立即执行,我们该怎么办,所以我们需要给紧急需要处理的消息一个绿色通道,这个绿色通道就是同步屏障概念。
同步屏障是什么?
同步屏障就是阻碍同步消息,只让异步消息通过。如何开启同步屏障呢?如下而MessageQueue#postSyncBarrier()我们看看它的源码
private int postSyncBarrier(long when) {
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) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
可以看到,Message 对象初始化的时候并没有给 target 赋值,因此, target == null 的 来源就找到了。上面消息的插入也做了相应的注释。这样,一条 target == null 的消息就进入了消息队列。开启同步屏障后,有是如何处理的呢,这回就要重点看下MessageQueue#next()
Message next() {
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
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) {
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;
}
}
}
从上面可以看出,当消息队列开启同步屏障的时候(即标识为 msg.target == null ),消息机制在处理消息的时 候,优先处理异步消息。这样,同步屏障就起到了一种过滤和优先级的作用。
同步屏障的应用场景
在View更新时,draw、requestLayout、invalidate等很多地方都调用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();
}
}
postCallback() 最终走到了 ChoreographerpostCallbackDelayedInternal()
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() 中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。
|