美女面试官问我:同步屏障和异步消息的运行机制
Posted 天才少年_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了美女面试官问我:同步屏障和异步消息的运行机制相关的知识,希望对你有一定的参考价值。
记得看文章三部曲,点赞,评论,转发。
微信搜索【程序员小安】关注还在移动开发领域苟活的大龄程序员,“面试系列”文章将在公众号同步发布。
1.前言
通过昨天的技术交流,天才少年成功闯过一关,来到二面现场。
2.正文
哎呀,怎么面试官跟昨天的是同一个人,不会是她对我有什么想法吧,嘿嘿,
是心动啊,糟糕眼神躲不掉,对你莫名的心跳,竟然停不了对你的迷恋~~
小伙子,今天的面试官又是我,是不是很激动,那么我们面试开始吧。
接着昨天聊得,今天讲一下消息的同步屏障和异步消息吧。
哈哈,Handler消息可是我的强项,想当年,那个夜晚。。。噗,怎么又回忆了。
Handler的Message简单来说可以分成三类:
1)普通消息----同步消息,我们平时使用的基本都是普通消息。
2)屏障消息----同步屏幕,Message消息中的target字段的值为null,当在消息队列中插入了屏障消息后,在屏障消息之后的普通消息将不会被执行,优先执行异步消息。类似于公司开会,屏障消息就是保安,当他站在人群中的时候,开完会,只能领导(异步消息)先走,等领导走完后,保安(屏障消息)取消后,我们这些打工者(普通消息)按照顺序依次回工位。
3)异步消息----调用setAsynchronous(true)设置为异步消息。
再深入一点吧
什么意思?是我的理解的那个意思吗,我有点惊慌失措。
你想什么呢?讲一下同步屏障如何创建。
同步屏障的创建很简单,通过调用MessageQueue的postSyncBarrier()即可,我们看下源码:
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();//可以看到此Message没有target
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) //根据when字段,插入到MessageQueue队列中
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;
上面关键代码已经注释过了,postSyncBarrier方法其实就是创建一个么有target的Message对象,然后根据when字段,插入到消息队列中。
那异步消息呢?如何创建的,消息屏障又是如何保证优先执行异步消息?
异步消息创建更简单,调用Message的setAsynchronous方法即可:
public void setAsynchronous(boolean async)
if (async)
flags |= FLAG_ASYNCHRONOUS;
else
flags &= ~FLAG_ASYNCHRONOUS;
至于消息屏障如何保证异步消息优先执行,这个肯定要看Looper对象的loop方法了,对于Handler机制不了解,可以看我前面一篇文章Android消息处理机制(Handler、Looper、MessageQueue与Message) ,这一篇文章就够了,looper方法会循环通过queue.next方法获取待处理的Message对象,那核心代码肯定在这个queue.next方法中,让我们来跟踪一下:
Message next()
//省略一些代码
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) //1.当target==null,是不是很熟悉,说明这个Message是同步屏障
do
prevMsg = msg;
msg = msg.next;
while (msg != null && !msg.isAsynchronous());//2.判断是不是异步消息,如果不是,则指针指向下一个,直到找到最近的异步消息
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;//3.找到待执行的异步消息,并返回他
else
nextPollTimeoutMillis = -1;
//省略一些代码
核心代码都做了注释,通过注释1会先判断,当前队列中是否包含屏障消息,如果包含则执行到注释2处,通过一个do/while循环查找最近的异步消息,然后返回该异步消息,如果异步消息都被分发,队列中没有异步消息,则会一直阻塞,等待唤醒。
那屏障消息如何移除呢?
屏障消息的移除同样是在MessageQueue中实现的,方法为removeSyncBarrier
public void removeSyncBarrier(int token)
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this)
Message prev = null;
Message p = mMessages;
//1.找到token对应的消息屏障
while (p != null && (p.target != null || p.arg1 != token))
prev = p;
p = p.next;
if (p == null)
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
final boolean needWake;
//2.从消息链表中删除该消息屏障
if (prev != null)
prev.next = p.next;
needWake = false;
else
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting)
nativeWake(mPtr);
核心代码都做了注释,通过注释1会找到token对应的消息屏障,然后把该消息屏障从MessageQueue中删除。
恩,你对Handler消息了解很深刻,今天有点晚了,view的创建流程,mChoreographer发送消息的流程,咱们后面再聊。
微信搜索【程序员小安】“面试系列(java&andriod)”文章将在公众号同步发布。
以上是关于美女面试官问我:同步屏障和异步消息的运行机制的主要内容,如果未能解决你的问题,请参考以下文章