Handler机制之MessageQueue源码分析

介绍:

一个用于保存(被Looper分发的)Message列表的低级类。与Native world的MessageQueue由紧密联系

MessageQueue类内部实现了两个Interface,一个静态内部类。

  • 接口IdleHandler在消息队列没有消息时使用,处理poll状态时的动作

  • 接口OnFileDescriptorEventListener在相应的文件状态改变(可读,可写,有错误)时被使用

  • 静态内部类FileDescriptorRecord,记录相应文件状态改变时的监视器OnFileDescriptorEventListener,在被native方法调用的dispatchEvents方法里被调用,执行监视器

先来了解一下构造方法和属性:

  //消息队列是否可以停止的标示,主线程是不可以停止,异步线程该可以停止,该标示为true
  private final boolean mQuitAllowed;

  // native code(C++)层的引用 ,当为0,则停止了该消息队列
  private long mPtr; 

   //当前消息队列的头信息(即需要最先处理的信息)
    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

    //用于标示next()方法是否阻塞,等待pollOne()非零时间
    private boolean mBlocked;
    
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        //调用native层创建相应的C++类对象,返回引用
        mPtr = nativeInit();
    }

接下来,了解Handler.sendMessage()传入Message,最终调用到enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {
        //message的target为空,即Handler为空,会跑出异常
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //Message若是在使用期间,会被跑出异常
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            //消息队列已经停止,则停止新加入message
            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;
            }
            //标记该message在使用状态
            msg.markInUse();
            msg.when = when;
            //获取到上一个Message
            Message p = mMessages;
            //是否唤醒的标示
            boolean needWake;
            //消息队列中没有信息,或者需要立即执行的message,或者当前消息的处理时间小于消息队列中头消息的处理时间,则将当前信息作为消息队列中的头消息。
            //与此同时,若是消息队列处于阻塞状态,则需要唤醒
            if (p == null || when == 0 || when < p.when) {
                //当上一个事件被阻塞,又来了一个新的Message,会唤醒消息队列
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {//当前传入信息的处理时间大于等于当前头信息的处理时间的情况。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //for循环方式,将当前传入的Message插入到消息队列中的合适位置
                for (;;) {
                    prev = p;
                    p = p.next;
                    //找到比当前传入的消息的处理时间大的Message
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //将传入的message插入到链表中合适位置
                msg.next = p; 
                prev.next = msg;
            }
            //若是需要唤醒,则调用Native层的唤醒,不在阻塞状态
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

总结:一个消息插入到消息队列中,若是插入到合适的中间位置,则不需要唤醒。若是插入到新的头部,与此同时,消息队列处于阻塞状态,则需要唤醒。

接下来,查看一下Looper.looper()方法中调用到的next():

 Message next() {
        //Native层的引用已经断开,消息队列已经停止,则返回null。
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        //用于标示第一次遍历,标示为-1
        int pendingIdleHandlerCount = -1; 
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                //线程先前处于睡眠状态,重新刷新那些等待处理的Binder进程间通讯请求,避免它们长时间得不到处理。
                Binder.flushPendingCommands();
            }
            //检查当前线程的消息队列中是否有新消息需要处理,若是nextPollTimeoutMillis为-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;
                if (msg != null && msg.target == null) {
                    // 循环方式,获取到下一个异步信息,异步消息不需要被同步阻塞
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        //当前时间小于消息的执行时间,则计算出需要等待时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //阻塞标记为false
                        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 {
                    // 没有更多信息,线程会进入睡眠等待状态,直到被其他线程唤醒。
                    nextPollTimeoutMillis = -1;
                }
                // 消息队列已经停止,调用Native层销毁方法
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //获取到空闲消息的监听器个数,当第一次遍历,且消息队列中没有消息或者处于阻塞状态,则进行空闲信息的回调操作
                if (pendingIdleHandlerCount <0&& (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //若是没有空闲消息的监听器或者本次next()方法已经发送过一次空闲消息,则不走以下逻辑操作
                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;
                boolean keep = false;
                try {
                    //回调空闲消息的监听器的queueIdle()
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                if (!keep) {//不在保持监听状态,则移除掉
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            //重置状态,每次Next()最多发出一个空闲信息
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

总结:MessageQueue的next()一次调用,最多只会发送一个空闲消息,触发空闲消息的监听器的queueIdle()

空闲消息的监听器IdleHandler的添加与移除方法:

    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);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值