欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Handler机制之MessageQueue源码分析

程序员文章站 2022-07-14 17:49:57
...

介绍:

一个用于保存(被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);
        }
    }