做Android开发的同学,应该都用过hanlder,具体的场景比如:在一个子线程中,做耗时操作,然后通过handler.sendMesssage发送消息到主线程,更新ui,有时也通过handler.sendMessageDelay,发送一条延时消息,那有没有思考过sendMesssage和sendMessageDelay内部是如何处理消息的?假设发送一条延时消息,马上发送一条正常消息,延时消息会阻塞正常消息吗,有经验的同学可能会知道,不会阻塞,那为什么呢?今天我们就来看看这个问题
看本文需要事先对handler的工作原理有个大概认识,熟悉MessageQueue,Looper,Message
handler的一系列send和post,最后都会调用handler.sendMessageDelayed
sendmessageDelayed里面只是简单处理了delayMillis,这里一个比较重要的参数时uptimeMillis,这里传入的是当前时间+delay时间
sendMessageAtTime内部调用了enqueueMessage
把当前handler对象,赋值给了msg.target,最后调用了queue.enqueueMessage,这个方法比较长,我们摘取重要的
message是一种单链表解构,mMessage会指向链表的第一个元素,链表中的元素以时间,从小到大排序
nativeWake这个方法很重要,它是实现唤醒的关键,needWake的赋值我们只看mBlocked,这个mBlocked我们后面讲
我们知道消息的处理是在Looper.loop中进行的,一个死循环,不断从messageQueue中获取消息,如果得到消息,那么交给对应的handler进行处理
messageQueue中获取消息是通过messageQueue的next方法,代码很长,我们精简一下
可以看到,next方法也是一个死循环,循环开始会调用nativePollOnce,传入nextPollTimeoutMillis,
如果nextPollTimeoutMillis=0,这个方法会立刻返回,不会阻塞
如果nextPollTimeoutMillis=-1,会一直阻塞,直到被唤醒,再返回
如果nextPollTimeoutMillis=某个正值,会一直等到timeout,返回,或者被唤醒,返回
如果nativePollOnce有返回之后,next就能返回一个message给Looper啦,现在的问题是nativePollOnce在什么时候有返回
知道这点之后,我们接着看next的实现
我们在方法底部,找到了mBlocked赋值为true,前边在enqueMessage中,我们有所提到,它是决定延时是否唤醒的关键
假设一种情况,我们创建一个主线程的handler,然后发送一个延时消息,再发送一个正常消息,这个时候MessageQueue的enqueMessage和next是怎么处理的呢?
回到enqueMessage的实现,发送延时消息时,由于消息队列中没有消息,所以p为null,mMessage指向当前消息,needWake被赋值为mBlocked,前面分析可以知道,如果这时候mBlocked为true,那么接着会执行nativeWake,接着nativePollOnce返回,然后去处理这个消息,这个mBlocked是true吗?
回答这个问题,我们需要回到next方法,看mBlocked唯一被赋值为true的地方,假设上一条消息全部处理完,也就是next方法返回了一个message给Looper.loop,Looper处理完这个消息以后,再次循环调用MessageQueue的next方法,这时候的nextPollTimeoutMillis为0,nativePollOnce会立刻返回,接着如果此时没有消息啦,那么nextPollTimeoutMillis被赋值为-1,mBlocked赋值为true,再次循环时,next方法进入阻塞状态
这里可以得出结论,mBlocked为true,进入阻塞,等待下次唤醒
这里我们就知道了,原来当我们发送delay消息的时候,mBlock为true,也就是nativeWake会被调用,next中nativePollOnce得已返回,然后处理这个delay消息,如何处理的呢
这时候msg!=null,nextPollTimeoutMillis赋值为当前时间和设置delay的when时间之间的差值,接着mBlocked赋值为true,下次循环进入阻塞状态
如果这时候,我们发送一条正常消息,由于消息队列中已经有了一条延时消息,那么这条消息会被插入到队列的第一个元素,然后调用nativeWake唤醒,next方法得以继续执行,nativePollOnce返回,去处理队列中的第一个元素,也就是我们发送的一条正常消息,处理完毕返回一个message给Looper,同时从队列里边删除该元素,Looper处理完消息,接着调用MessageQueue的next方法,去重新计算delay的时间
好啦,到这里,我们应该比较清楚delay消息和正常消息怎么处理的啦