android 中 Handler 模块,MessageQueue 源码分析

MessageQueue 是线程队列,作用接收 Handler 添加进来的 Message,按照规则排好序,同时对外(Looper)提供最近的 Message。队列里面装的是 Message,我们看一下最简化的 Message 

    final class Message implements Parcelable {
        Message next;
        ...
    }
我们看到了 Message 里面有个成员变量 next,也是 Message 类型,看到这里,就明白了 Message 可以形成一个单链表数据结构,在 MessageQueue 中,Message 正是以单链表结构存储数据的。 我们重点看一下 enqueueMessage() 和 next() 方法,这两个一个是添加 Message,一个是对外提供最近的 Message。

    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;
            }
            ...
        }
        return true;
    }
enqueueMessage() 中 其那面是一些校验,通过这个方法添加的 Message,必须要有 Handler 与之相对应,默认 msg 是没有使用的,这里如果使用了,就说明添加重复了,紧接着就是mQuitting 属性判断该线程队列有没有退出,如果退出,则会回收 Message,停止后续逻辑操作。 代码简化后,

    boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            ...

            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; // invariant: p == prev.next
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

long when 这个参数,实际上可以理解为: SystemClock.uptimeMillis() + delayMillis, uptimeMillis() 意思是手机开机到现在的时间,delayMillis 是要延迟的时间,延迟0秒、3秒或5秒,之所以用开机时间没有使用手机本地时间,是因为本地时间是可以调整的,开机时间不可随意调整。 对 Message 进行插入队列时,用 synchronized 关键字来保证线程安全,msg.markInUse() 改变msg的标识,意思正在使用; msg.when = when 是给这个msg添加时间标识;mMessages 是 Messages 类型,是单链表的 head,现在读一下代码逻辑:

一、假如说这个是首次添加新来的 Message A,延迟3秒执行,那么此时 Message p = mMessages 中,p 和 mMessages 的值都为 null,此时进入到if语句中,这时候新添加的 msg 变为了 head 信息,即 mMessages = msg, mBlocked 默认值是 false,即 needWake 值也为 false, if (needWake) {nativeWake(mPtr);} 意思是如果需要唤醒,就用 nativeWake()调用底层的方法,从睡眠中唤醒。 二、假如这时候又添加一个 Message B,延迟0秒即马上执行,此时, mMessages 和 p 为 A,由于此时 when 比着 p.when 要小3秒,所以也走到了if语句里,msg.next = A,mMessages = B, needWake 仍旧是 false,这样, B 就变成了 head,此时链表中有两个元素,按顺序是 B--A,这时候 next() 方法就会执行,但我们假设它先不执行,继续看我们当前的方法。 三、假如这时候又添加一个 Message C,延迟1秒执行,此时 mMessages 和 p 为 B,p 不为null, when > p.when,所以不满足if条件,走到了else里面,此时 needWake 为false,我们看一下for循环,里面的语句 prev = p; p = p.next; 此时,prev 为 B,p 为 A,此时,if (p == null || when < p.when) 条件中,C 的when 小于A的when,C是延迟1秒,A是延迟3秒,此时满足条件,break跳出for循环,注意 msg.next = p 意思是 C 的 next 属性为 A,这样 C 和 A 就形成一个链 C--A, prev.next = msg 意思是此时prev为 B,即 B 的next由A变为了B,这时候整体链表就是 B--C--A。其他的插入 Message 都一个道理,这里需要一些数据结构知识。常用的通过Handler添加msg就是这样了。

Handler执行消息是通过 Looper 从 MessageQueue 中获取消息的,next() 方法就是对外提供最近的 Message,我们看一下这个方法

    Message next() {
        ...
        int nextPollTimeoutMillis = 0;
        for (;;) {
           ...
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                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) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        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 {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
            ...
        }
    }

简化的代码,先看上半部分,for循环中,Binder.flushPendingCommands() 方法被调用说明后面的代码可能会引起线程阻塞,nativePollOnce(ptr, nextPollTimeoutMillis)根据nextPollTimeoutMillis的值是阻塞,0的话不阻塞,-1 一直阻塞,正数 是阻塞的时间,比如 nextPollTimeoutMillis = 3000,则阻塞 3000 毫秒。 这里也有 synchronized 关键字,防止并发引起的数据错误,正常情况下,Message 都是有对应的 Handler,msg.target == null 这种情况是系统层会用到,应用层一般用不到,网上有更详细的介绍,这里跳过,按照上面添加的三个 Message 为  B--C--A,此时 Message msg = mMessages, msg 为 B,if (now < msg.when) 判断中,now的值为 long now = SystemClock.uptimeMillis(),由于 B 是没有延迟时间,所以此时两个值相等,走到了else中, 此时 prevMsg 值为 null,则 mMessages = msg.next, 此时 msg.next 为 C,则 mMessages 即 head 头部元素,也为 C,msg.next =null 是切断链接,msg.markInUse() 作用是表示该msg在使用,看下面一行代码 return msg; 这个msg就被 Looper 交割给Handler,然后执行 dispatchMessage(Message msg) 方法消耗了。 我们注意,Looper中调用 queue.next()的这个for循环是无限,也就是说,会继续调用 next() 方法,这时候,队列中的列表是 C--A,此时代码 nextPollTimeoutMillis = 0,注意
synchronized里面的代码,msg = mMessages ,它俩都是 C,if (now < msg.when)  msg是延迟1秒执行,满足条件,这时 nextPollTimeoutMillis = 1000,本次for循环基本结束,重新再次循环,这时候,nativePollOnce(ptr, nextPollTimeoutMillis) 中意味着 nativePollOnce(ptr, 1000),阻塞1秒,1秒后恢复,重新走到 synchronized 模块,此时 msg =mMessages ,它俩仍是 C,if (now < msg.when) 判断不符合条件,走else里面,这时候,我们会发现逻辑和上面的 B 执行时一样,就是这样。

重复执行,A也被执行完后,这时候 if (msg != null) 就不满足了,看看else中, nextPollTimeoutMillis = -1,继续for循环,然后就是 nativePollOnce(ptr, -1); 这时就一直阻塞,知道被唤醒。

    Message next() {
        ...
        for (;;) {
            ...
            synchronized (this) {
                ...
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

mQuitting 是退出的标识,如果没有主动调用 quit()方法,这里就不会执行。 mIdleHandlers 是成员变量,是一个接口的集合,通常这个集合是空集合,if (pendingIdleHandlerCount< 0&& (mMessages == null || now < mMessages.when)) 这个判断,如果当前没有 Message 或者没有马上要执行的 Message,比如只有几个要延迟几秒执行的 Message,这时候就满足if语句判断,获取当前的接口集合的个数,如果为空,则 mBlocked = true,它的意思是阻塞, continue 标识跳出此次循环,进入下一次,这就是上面说的 nextPollTimeoutMillis 获取值后,还能传递给下一次的原因,在这里跳出了循环; 如果该集合有元素,则在下一步中把内容填充到 mPendingIdleHandlers 数组中,再下面又是一个for循环,这时候会遍历数组中的每个元素,然后执行 keep = idler.queueIdle() 回调,这个接口中的方法有个 boolean 的返回值,如果返回值为 false,在执行完当次操作后,会把它从 mIdleHandlers 集合中移除,下次的时候就不会执行了,否则等下一次再触发次方法时,回调还会被执行。最下面是把两个属性清零。 由于比较长,我是分开写的,正常应该是一气呵成。

上面说了,这里有个退出队列的操作 

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;
            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }
            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

首先校验 mQuitAllowed,是否允许退出,这个值是 Looper 构造方法中传递的,

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
也就是说,只有允许退出的队列,才能调用 quit() 方法,继续看,同步代码块,是一个标识,重点看下面的if语句,安全退出和非安全退出的区别

    private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }
    
    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }

非安全退出,removeAllMessagesLocked()  遍历每个 Message,然后调用 Message 的 recycleUnchecked() 方法,Message会把成员变量引用释放,一切属性归原,包括是否正在使用标识。 安全退出 removeAllFutureMessagesLocked() 操作就多一点,如果当前头部的 Message 时间大于当前时间,也就是说所有的 Message 都是延迟执行的,那么此时就把它们全都回收,链表置空;如果有需要马上执行的,比如链表 1---2--3--4中,1和2是马上执行的,3和4是延迟几秒执行的,这是后通过for循环,把头部 head 的指针,从1移到3,并切断链表中2和3的链接,然后把3和4回收,1和2仍然可以被 next() 方法调用执行。

如果从队列中移除某个 Message 或全部的 Message,可以调用 removeMessages() 方法,里面也是寻找到Message后,把它从链表中移除,还是要懂数据结构,他们被移除时,都会调用Message.recycleUnchecked() 方法,回收对象。其实被Handler执行的 msg,也会被回收,我们看看 Looper 中 loop() 方法,底部有一个 msg.recycleUnchecked(),也就是说,执行完
msg.target.dispatchMessage(msg) 后,就会执行回收 Message 操作。

    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) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

    public void removeSyncBarrier(int token) {
      synchronized (this) {
            Message prev = null;
            Message p = mMessages;
        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;
        if (prev != null) {
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }                
    }    
    }


    
这两个方法都是 @hide 级别,是给系统使用的,它会往队列中插入一个 Message,但这个 Message 没有对应的 Handler,它的作用是  next() 方法中执行 do... while... 操作,这两个方法也是添加和移除 Message,关键还是数据结构,数据结构懂了,这里面的方法就好理解了。

    public boolean isIdle() {
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            return mMessages == null || now < mMessages.when;
        }
    }

    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }
这三个方法是对接口 IdleHandler 的管理和使用,比如我们在Activity中,如果想等UI界面刚绘制完就执行某些操作,可以通过 addIdleHandler() 方法添加回调,等onResume()中界面绘制完后,马上就会执行mIdleHandlers集合中的回调, isIdle() 方法的意思是如果没有等待处理的Message,就返回true。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值