再论Handler—Handler事件分发、Handler线程切换原理解析

本文深入探讨Android中的Handler机制,解析Handler、Looper、MessageQueue和Message的原理与关系,阐述Handler事件分发和线程切换机制,揭示线程切换的本质。

#再论Handler—Handler原理解析

标签(空格分隔): Android架构师之路


Handler,无论你菜鸟还是像我一样的老菜鸟,都无法避免这个亘古不灭的话题,只有了解学会Handler的原理,你才能变成一个出的厅堂下得厨房的优秀的老菜鸟。其实同类优秀的帖子已经很多了,但是为了强化记忆我的理解,还是想再次记一下,我心中的Handler。

要介绍Handler,就必须要介绍与它息息相关甚至“同为一体”的其它几个类:Looper、MessageQueue、Message。所以本篇文章就分为三个部分:

  • Handler、Looper、MessageQueue、Message原理及关系。
  • Handler事件分发原理。
  • Handler线程切换原理。

1. Handler、Looper、MessageQueue、Message原理及关系。

1.1 Message

Message是定义一个包含描述和可以发送到Handler的任意数据对象的消息。

我们先看Message的几个关键变量:
在这里插入图片描述
target,就是负责发送且分发它的Handler;
callback,是一个Runnable回调;
next,下一个消息池中的它之下的message。

现在有3个问题:

  • 1.target从哪来来,作用是什么?
  • 2.callback又是什么及其作用;
  • 3.还有next是什么?

我们先不着急给出答案,接着往下看。

1.2 MessageQueue 消息队列

它持有并维护了当前线程的Looper分发的消息列表。消息并不是直接而是通过Handler 关联的Looper入队列的。

MessageQueue看上去是消息队列,然而它实际上只是消息队列的管理者。消息列表是它持有的Message mMessage,是由Message组成的单链表结构。
我们看它管理消息的两个核心方法:

  • boolean enqueueMessage(Message msg, long when) ;
  • Message next()。
1.2.1 boolean enqueueMessage(Message msg, long when) ,消息入队列;

先上代码:

 boolean enqueueMessage(Message msg, long when) {
   
   
        if (msg.target == null) {
   
   
            throw new IllegalArgumentException("Message must have a target.");
        }
    

        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) {
   
   
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
   
   
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
   
   
                nativeWake(mPtr);
            }
        }
        return true;
    }

可以看到,在第一行就校验了msg.target是否为null,由此可知,msg必须关联(持有)负责分发它的handler;
最重要的是for循环里入队的这部分,通过逐个和消息列表里消息的发生时间对比,找到与当前入队的message的 -分发时间-之前它时间最接近的preMsg,将mesage设置到preMsg的next(此处也解答了问题3:message的next是什么),最终得到的是一个按时间先后顺序排列的链表;
此外,入队操作还做了一件事情:当消息入队时候,如果队列为空的或需要立即分发当前消息分发时间-早于消息队列中最近一个需要分发的消息的时间的(而此时MessageQueue的next()方法的循环是处于阻塞状态的),就调用一个native方法nativeWake()唤醒取出消息的next()(next方法的循环会被nativePollOnce()native方法阻塞)循环。

1.2.2 Message next();

先上源码

 Message next() {
   
   
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
   
   
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
   
   
            if (nextPollTimeoutMillis != 0) {
   
   
                Binder.flushPendingCommands();
            }
              //-1-直接休眠一定的时间;
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
   
   
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                  //-2-找出下一个需要分发的消息。
                if (msg != null && msg.target == null) {
   
   
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
   
   
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
   
   
                    //-3- 如果时机未到,即当前时间还不到消息的处理时间,就计算休眠时间,
                    //反之,返回这个待处理的消息。并将休眠状态mBlocked置为false
                    if (now < msg.when) {
   
   
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        // 还没有到下一个消息触发时间,那么在它之前休眠(休眠一段时间)
                        nextPollTi
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值