Handler源码流程梳理

Handler的工作流程

 

需要搞明白Handler的流程,就是从sendMessage一直到handleMessage的过程。

 

Handler.sendMessage()

首先来看sendMessage,从图中就可以看出来,它就是把货物放到了这个传送带上来。

在Handler.java这个类里面,有很多函数用来发送消息。其中send开头的就有这些:

然后还有post开头的:

也就是说在这个类里面,就有这么多的函数用来发送消息的。它们之间有什么关系呢?

看这个图,也就是说任意一个send,无论它的流程怎么跑,最后它都会调度在了enqueueMessgae这个方法中。

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

而Handler.enqueueMessage()又调用了MessageQueue.enqueueMessage(),所以,这一系列的函数,最终的目的都是在消息队列中添加了消息。

 

MessageQueue.enqueueMessage()

在enqueueMessage()中,它维持了一个消息队列,这个消息队列其实是一个优先级队列。

优先级队列

假如有一个M4要插入到这个队列中去,它首先要轮训M1-M2-M3这个队列;

然后分别比较M4与M1、M2、M3...;

看到M4刚好比M2大,且比M3小,就插入到这个队列中去,这个队列也就是个有序的队列

在源码里面,MessageQueue.java中的enqueueMessage方法里面有一个for循环,他的作用就是根据时间when进行一个排序。

                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }

看这个for循环,就是对单链表的一个轮询,其关键点就在于找到循环中的break。

enqueueMessage(Message msg, long when)

when是传入的值,代表这个消息什么时候被执行,如果when等于0,那就是立刻执行。

首先,它会拿到第一个节点p。

p = p.next;

将第一个节点的时刻p.when,与我们刚加入的这个消息的时刻when对比。

when < p.when

如果新加入的消息的时刻小于前一个节点的时刻,那么新加入的消息就要比前一个节点的消息先执行,那么,新加入的消息就要插入到这个消息的之前,所以就执行break停止查找,然后就将消息插入到前面。

msg.next = p;

它沿着这个序列进行轮询,先查询第一个消息的执行时间,如果第一个消息的执行时间早于新插入的这个消息的节点,再去查找第二个节点,这时候发现第二个消息的发送的时间又比这个时间早,他就会再去查找第三个,他发现第三个消息的执行时间要比插入的消息的执行时间晚,这时候,就可以把这个消息插入到了这个消息的队列中来,这样就形成了一个按照时间先后进行排序的有序队列。

但我们添加的消息基本上是不相同的,除非是0。

因为在sendMessageDelayed中,它的延迟时间是系统时间加上delay的时间,而系统时间是每时每刻都在增大的,就像是for循环每走一遍,他的时间也会增大。

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

而如果等于0,则直接插入。

            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            }

 

MessageQueue.next()

前面说到往MessageQueue这个消息队列中存消息,就一定会有从这个消息队列中取消息,next()函数就是返回了一个Message。

    Message next(){
        ....
    }

next()函数是如何取的消息,要先看下这个if语句。

                    //开机到现在的毫秒数如果小于msg.when则代表还未到发送消息的时间
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        // 虽然有消息,但是还没有到运行的时候
                        //计算还有等待多久,并赋值给nextPollTimeoutMillis
                    	//设置下一次查询消息需要等待的时长
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                    	//代表当前没有阻塞
                        mBlocked = false;
                     // 获取msg并且删除该节点 
                        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;
                    }

他先用 当前时刻now 和 它所拿到的队头的时刻msg.when 进行对比,now < msg.when 也就是说,消息队列的第一个节点还没到可以执行的时刻,所以就wait等待。

反之,如果 now >= msg.when ,说明现在的时刻已经到了可以发送消息的时刻,就可以把消息返回出去。

 

Looper.loop()

有没有想过这样一个问题:上面所说的next()函数由谁来调用的呢?

对,就是这个loop()函数,它不断的调用next()函数,通过调用next()函数去轮询我们的MessageQueue,让这个MessageQueue这样一个传送带滚动起来的。

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

           ......
        }

如果这个消息队列为空的时候,会等待(MessageQueue.next())。

        for (;;) {

            .....

            //native的方法,在没有消息的时候回阻塞管道读取端,只有nativePollOnce返回之后才能往下执行
            //阻塞操作,等待nextPollTimeoutMillis时长
            nativePollOnce(ptr, nextPollTimeoutMillis);

            .....

        }

所以它就一直会block在这里(Looper.loop())。

Message msg = queue.next(); // might block

被block之后就不会走return,不会走return,这个for循环(死循环)就一直在这里等待。

当Looper获取到了这个消息之后,就会调用dispatchMessage这个函数。

                msg.target.dispatchMessage(msg);

而dispatchMessage又会调用Handler.dispatchMessage()来处理消息,这时候就完成了消息链条的整个流程。

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值