zoukankan      html  css  js  c++  java
  • Android Handler机制(二)---MessageQueue源码解析

    MessageQueue

    1.变量

        private final boolean mQuitAllowed;//表示MessageQueue是否允许退出
        @SuppressWarnings("unused")
        private long mPtr; //mPtr是native代码相关的
        
        Message mMessages; //表示消息队列的头Head
        private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
        private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
        private IdleHandler[] mPendingIdleHandlers;
        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; 

    mQuitAllowed表示MessageQueue是否允许退出,系统创建的UI线程的MessageQueue是不允许的,其他客户端代码创建的都是允许的;

    mPtr是native代码相关的,指向C/C++代码中的某些对象(指针),其他一些nativeXXX()相关的函数本文暂不做分析;

    mMessages表示消息队列的头Head;

    mIdleHandlers是IdldHandler接口的ArrayList, mPendingIdleHandlers是数组版本,在后面的代码中会将ArrayList的内容拷贝到它里面;

    mQuitting表示当前队列是否处于正在退出状态;

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

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

    IdleHandler callback接口:

    public static interface IdleHandler {
            /**
             * Called when the message queue has run out of messages and will now
             * wait for more.  Return true to keep your idle handler active, false
             * to have it removed.  This may be called if there are still messages
             * pending in the queue, but they are all scheduled to be dispatched
             * after the current time.
             */
            boolean queueIdle();
        }
    public void addIdleHandler(IdleHandler handler) {
            if (handler == null) {
                throw new NullPointerException("Can't add a null IdleHandler");
            }
            synchronized (this) {
                mIdleHandlers.add(handler);
            }
        }
     public void removeIdleHandler(IdleHandler handler) {
            synchronized (this) {
                mIdleHandlers.remove(handler);
            } 

    IdleHandler接口表示当MessageQueue发现当前没有更多消息可以处理的时候则顺便干点别的事情的callback函数(即如果发现idle了,

    那就找点别的事干)。callback函数有个boolean的返回值,表示是否keep。如果返回false,则它会在调用完毕之后从mIdleHandlers

    中移除。这里让我们来看一个具体的例子(实现),ActivityThread.java里的一个内部类,代码如下:

    final class GcIdler implements MessageQueue.IdleHandler {
            @Override
            public final boolean queueIdle() {
                doGcIfNeeded();
                return false;
            }
        } 

    这是一个gc相关的IdleHandler,即如果没有更多的消息可以处理就会抽空doGcIfNeeded(),最后返回false表示不保留在mIdleHandlers

    中,即用一次就扔了,只执行一遍。

    MQ的构造方法:在Looper中就是调用的这个构造方法。

    MessageQueue(boolean quitAllowed) {
            mQuitAllowed = quitAllowed;  //true是允许推出
            mPtr = nativeInit(); //这是一个本地方法
        }

    MQ的构造方法简单的调用了nativeInit()来进行初始化,这是一个jni方法,也就是说,可能是在JNI层维持了它这个消息队列的对象。在message中有好多native方法,可以看出message是比较底层的一个类。

    接下来看MessageQueue的核心方法,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();
                }
    
                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;
            }
        }

    首先初始化2个接下来要用到的变量,紧接着进入无限for循环中,其某次循环主要做这么几件事情:

    1. 如果nextPollTimeoutMillis != 0的话,调用Binder.flushPendingCommands();

    2. 调用nativePollOnce(mPtr, nextPollTimeoutMillis);

    3. 进入一个大的同步块,尝试获取一个可以处理的消息,具体做法是,记录当前时间now,初始化变量prevMsg为null,msg为mMessges;

    如果msg是一个sync barrier消息,则直奔下一个asynchronous消息(这之间的所有同步消息会被本次循环忽略,也就是说遇到这种情况,

    next方法会从找到的异步消息的位置开始尝试获取一个可以处理的消息并返回),同时更新prevMsg,msg的值;

    4.当退出此do...while循环的时候msg可能为空(走到队列尾了),或者成功找到了一个这样的(异步)消息。

    5.如果是到队尾了即msg==null,则表示没更多的消息了,设置nextPollTimeoutMillis = -1;否则当now<msg.when(msg的时间还没到),设置一个合理的等待时间,即调用

    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

    6.当msg到了该处理的时间了,也就是说我们找到了这样一个消息可以返回了,设置mBlocked为false,将msg从mMessages队列中取出来(类似单链表的删除操作),并执行

    msg.next=null、msg.markInUse(),返回msg。

    7.如果到这一步了还没return的话,那说明还没有可以处理的消息,检查下队列是否要求退出了,如果是执行dispose(),返回null。当Looper的loop方法看到null的message的时候会退出loop。

    8.接下来既然没消息可以处理,那就该处理IdleHandler了。如果pendingIdleHandlerCount小于0(注意其在第一次进入for循环是被初始化为-1)且没更多的消息需要处理,设置pendingIdleHandlerCount=mIdleHandlers.size();

    9.如果pendingIdleHandlerCount还是<=0的话,表示没有idle handler需要执行,

    设置mBlocked为true,接着进入下次循环。

    10.接下来就是根据mIdleHandlers来初始化mPendingIdleHandlers。退出同步块后我们就剩下最后一件事了,那就是run Idle handlers。一个for循环用来做这就事情,在循环内如果IdleHandler没必要保留,则会从mIdleHandlers中移除。

    11. 最后重置pendingIdleHandlerCount为0(也就是4只会在第一次循环的时候执行一次),将nextPollTimeoutMillis设为0,因为当我们在

    执行4的时候,新的Message可能已经到来了,所以我们需要立即开始(不需要等待)下次循环来检查。

    **上面很长,简单点说就是:next方法取出下一个Message(从头部取),如果没有Message可以处理,就可以处理下IdleHandler。

    看完了next()方法,接下来我们来看enqueue()方法:

    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;
                }
    
                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;
        } 

    在后边介绍Handler的时候,可以看到,许多跟message(包括runnable)相关的操作,最终都delegate给了MessageQueue的enqueue方法。

    和任何方法一样,在enqueue之前,也都是对参数msg的检查,比如msg如果在使用中,或者msg的target是null,都会抛出AndroidRuntimeException,进行完条件检查后,会进入真正的处理逻辑。接下来的操作类似在一张单链表中插入一个元素:进入同步块

    1. 如果此队列处于正在退出的状态则不能在往里入队了,不能插入元素了,在这种情况下会抛出RuntimeException,然后return false,

    表示失败了;

    2. 接下来表示队列的状态ok,设置msg的when字段,临时变量p指向队列头;(必要的初始化,准备工作)

    3. 如果队列是空的或when==0或when<p.when,也就是说要插入的这个message应该在第一个位置也就是队首,那么它将是新的Head,将它和原先的队列连接起来;

    4. 否则插入将发生在队列中间的某个位置(有可能是队尾),将msg插在第一个p的前面,p满足这个条件(p == null || when < p.when)。

    最后退出同步块,返回true,表示操作(入队)成功。

    **messageQueue中的元素是按序按时间先后插入的(先执行的在前)。

    removeMessages():

    接着我们来看3个类似的removeMessages()方法,

     void removeMessages(Handler h, int what, Object object) {
            if (h == null) {
                return;
            }
    
            synchronized (this) {
                Message p = mMessages;
    
                // Remove all messages at front.
                while (p != null && p.target == h && p.what == what
                       && (object == null || p.obj == object)) {
                    Message n = p.next;
                    mMessages = n;
                    p.recycleUnchecked();
                    p = n;
                }
    
                // Remove all messages after front.
                while (p != null) {
                    Message n = p.next;
                    if (n != null) {
                        if (n.target == h && n.what == what
                            && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycleUnchecked();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        }
    
        void removeMessages(Handler h, Runnable r, Object object) {
            if (h == null || r == null) {
                return;
            }
    
            synchronized (this) {
                Message p = mMessages;
    
                // Remove all messages at front.
                while (p != null && p.target == h && p.callback == r
                       && (object == null || p.obj == object)) {
                    Message n = p.next;
                    mMessages = n;
                    p.recycleUnchecked();
                    p = n;
                }
    
                // Remove all messages after front.
                while (p != null) {
                    Message n = p.next;
                    if (n != null) {
                        if (n.target == h && n.callback == r
                            && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycleUnchecked();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        }
    
        void removeCallbacksAndMessages(Handler h, Object object) {
            if (h == null) {
                return;
            }
    
            synchronized (this) {
                Message p = mMessages;
    
                // Remove all messages at front.
                while (p != null && p.target == h
                        && (object == null || p.obj == object)) {
                    Message n = p.next;
                    mMessages = n;
                    p.recycleUnchecked();
                    p = n;
                }
    
                // Remove all messages after front.
                while (p != null) {
                    Message n = p.next;
                    if (n != null) {
                        if (n.target == h && (object == null || n.obj == object)) {
                            Message nn = n.next;
                            n.recycleUnchecked();
                            p.next = nn;
                            continue;
                        }
                    }
                    p = n;
                }
            }
        } 

    这3个方法只是remove的条件不同,其主逻辑都是相同的,即从队列中删除所有匹配的元素。总体思想都是先从队首删除,如果删除了则队首

    指向接下来的元素,重复这个过程,直到第一个不匹配的元素出现。接着从这个元素之后(after front)开始查找并删除,

    方法是链表删除后一个节点的方法,即p.next=nn。注意这里都是删除所有匹配的消息,而不是第一个匹配的。

    最后看下队列的quit()方法:

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

    quit方法根据所传参数safe的值,有2种不同的退出策略,如果是safe的退出,则执行removeAllFutureMessagesLocked(),

    其内部的逻辑为如果队首的元素还没到期那说明队列中其他所有的元素也都没到期,所以等同于删除所有的消息即调用

    removeAllMessagesLocked();否则遍历队列找到第一个p.when>now这样的message元素p,(更新队列的尾部p.next=null,

    缩短队列)从p开始一直到队列结束都是要被删掉的元素,全部删除之;如果是unsafe的退出,则所有message都直接被删除并

    回收即调用removeAllMessagesLocked()。

          Message是链表结构,MessageQueue中的好多操作都是基于链表,看起来困难的话,可以画画图,就很清晰啦。。。

    Message的源码分析:http://www.cnblogs.com/jycboy/p/5786551.html

    MessageQueue源码解析:http://www.cnblogs.com/jycboy/p/5786682.html

    Handler源码解析:http://www.cnblogs.com/jycboy/p/5791457.html

    Message源码解析:http://www.cnblogs.com/jycboy/p/5786551.html

    转发注明出处: http://www.cnblogs.com/jycboy/p/5786682.html

  • 相关阅读:
    Ajax让网站与时俱进
    暑期小学期一周总结至7月5号
    17级软件工程交流活动体会
    IE8兼容IE7模式代码:XUACompatible详解
    SQLiteHelper
    Microsoft Office OneNote 2007的安装与激活
    白石洲,让人遗憾的历史的产品
    .NET4的SQLite配置
    ADO.NET Entity Framework学习笔记(5)ESQL查询语句
    ADO.NET Entity Framework学习笔记(3)ObjectContext对象
  • 原文地址:https://www.cnblogs.com/jycboy/p/5786682.html
Copyright © 2011-2022 走看看