Android消息机制---MessageQueue的工作原理

    一、简述:

    Google官方对MessageQueue类的描述:

    Low-level class holding the list of messages to be dispatched by a Looper.  Messages are not added directly to a MessageQueue,
but rather through Handler objects associated with the Looper.
   大致意思就是说:MessageQueue是比较低层的类,是持有Message(在Looper中派发)的一个链表,但Message不是直接添加到MessageQueue中的,而是通过与Looper相关联的Handler来进行的。

   二、源码解析:

    先来了解一下MessageQueue的一些属性信息:

    // True if the message queue can be quit.
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private long mPtr; // used by native code

    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private boolean mQuitting;

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

    // The next barrier token.
    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
    private int mNextBarrierToken;
   boolean  mQuitAllowed: 其含义与 Looper.prepare(boolean quitAllowed) 中参数含义一直,是否允许MessageQueue退出;

   long mPtr: Android MessageQueue 是通过调用 C++ native MessageQueue 实现的,这个 mPtr 就是指向 native MessageQueue;

   mMessage: 表示存储消息链表的头Head;

   ArrayList<IdleHandler> mIdleHandler:dldHandler接口的ArrayList, mPendingIdleHandlers是数组版本,在后面的代码中会将ArrayList的内容拷贝到它里面;

   boolean mQuitting: 当前MessageQueue是否正在终止;

   boolean mBlocked: 表示next()调用是否被block在timeout不为0的pollOnce上;

   int mNextBarrierToken: 表示下一个barrier token,barrier用target==null, arg1==token的Message对象表示;

   再来看一下构造方法的代码:

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
    在构造方法中首先为变量mQuitAllowed初始化,紧接着调用C++的nativeInit()方法为mPtr初始化。下面是nativeInit()方法的代码:

static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {  
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();  
    if (!nativeMessageQueue) {  
        jniThrowRuntimeException(env, "Unable to allocate native queue");  
        return;  
    }  
  
    nativeMessageQueue->incStrong(env);  
    android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);  
}  
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,  
        NativeMessageQueue* nativeMessageQueue) {  
    env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,  
             reinterpret_cast<jint>(nativeMessageQueue));  
}  
    nativeInit() 方法创建 NativeMessageQueue 对象,并将这个对象的指针复制给 Android MessageQueue 的 mPtr。关于C++中的nativeXXX方法不做过多分析,只要明白mPtr为native层的MessageQueue的指针即可。

    enqueueMessage()方法的源码:

boolean enqueueMessage(Message msg, long when) {
        //这里的msg.target指的就是发送该条消息的handler
        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) {
            
            ......
           
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
           
            //插入消息到链表的头部:MessageQueue实例的头结点Message进行触发时间先后的比较,
            //如果触发时间比现有的头结点Message短,或者现有的链表头部Message触发时间为0,
            //亦或是当前MessageQueue中消息链表为空,则这个新的Message作为整个
            //MessageQueue的头结点,如果阻塞着,则立即唤醒线程处理
            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 {
                //插入消息到链表的中间:如果插入消息到链表头部的条件不具备,则依次                
                //循环消息链表比较触发时间的长短,然后将消息插入到消息链表的合适位置。接着
                //如果需要唤醒线程处理则调用C++中的nativeWake()函数.
                // 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;
    }
从上面的源码来看enqueueMessage()函数:主要向单链表中作插入操作。


next() 方法是在 Looper.loop() 中被调用的,Looper 在获得要处理的消息之后就会调用和消息关联的 Handler 来分发消息,我们再来看一下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.
        //mPtr:Android MessageQueue 是通过调用 C++ native MessageQueue 实现的,
        //这个 mPtr 就是指向 native MessageQueue;
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        //无限for循环
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            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) {
                    // 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;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            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);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

从上面的源码中我们知道:队列被激活之后,首先判断队首是不是消息屏障,如果是则跳过所有的同步消息,查找最先要处理的异步消息。如果第一个待处理的消息还没有到要处理的时机则设置激活等待时间;否则这个消息就是需要处理的消息,将该消息设置为 inuse,并将队列设置为非 blocked 状态,然后返回该消息。next()方法是一个无线循环的方法,如果消息队列中没有消息,那么next()方法会一直阻塞,当有新消息到来时,next会将这条消息返回同时也将这条消息从链表中删除。

三、总结

    在Android中MessageQueue主要包含两个操作:插入和读取。读取操作本身会伴随着删除操作,插入和读取对应的方法分别是enqueueMessage()和next(),其中enqueueMessage()的作用是往消息队列中插入一条消息,而next()是从消息队列中取出一条消息并将其从消息队列中移除。在Java中基本的数据结构就是数组和链表,其他的数据结构都是同过数组或者链表的封装或者是二者的组合,其实MessageQueue也不例外,它是通过一个单链表的数据结构来维护消息列表,这主要是因为单链表在插入和删除的操作效率要优于数组。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值