handler源码分析之MessageQueue

MessageQueue由Looper派发消息列表,不能够直接添加Message到MessageQueue,而是通过与Looper关联的Handler对象添加,可以使用Looper#myQueue()检索当前线程的MessageQueue.

总结

  • MessageQueue消息队列,通过Handler调用enqueueMessage方法把Message添加到队列中,根据when循环遍历队列,并把消息放入指定的位置,校验是否需要唤醒线程。MessageQueue有一个消息屏障的概念,它本质上一个Message,与其不同的是Target==null,并且args是token,消息屏障的作用是过滤同步消息,拦截异步消息。
    Looper在轮询时调用的是消息队列next方法,把Message传递给Looper,next方法首先黑判断队列中是否有消息屏障,如果有只发送异步消息,如果没有,就依次进行发送,当消息队列中没有Message时,next方法阻塞线程,等待新的消息到来,在阻塞线程之前,此方法会阻塞线程前,会执行IdleHandler,所谓的阻塞其实就是不断的轮询新的消息。
    当调用quit方法是,mQuitting会变成true,MessageQueue将要被关闭,可以根据传递的参数safe选择是否安全的关闭,所谓安全的关闭是指,以当前的时间为节点,大于当前时间的Message会被删除小于当前时间节点的消息会被保留到执行完毕。而不安全的退出消息队列就是把整个mMessage设置为null。然后在looper轮询时next方法返回为null,通知looper消息队列已经关闭,Looper可以关闭了。
    Handler线程里实际上有两个无限循环体,Looper循环体和MessageQueue循环体,真正阻塞的地方是在MessageQueue的next()方法里

变量 Message mMessages

在MessageQueue中所有的Message都是以链表的形式组织在一起的,mMessages保存了链表的第一个元素,也可以说是链表本身。

  • private long mPtr;
    该变量用于保存native代码中MessageQueue的指针。

  • mQuitAllowed
    该变量用于标识消息队列是否可以被关闭,主线程的消息队列不可关闭。

  • private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    当Handler线程处于空闲状态的时候(MessageQueue中没有其他Message时),可以利用它来处理一些事物,该变量就是用于保存这些在空闲时候要处理的事物。

  • private IdleHandler[] mPendingIdleHandlers
    用于保存将要被执行的IdleHandler。

  • private boolean mQuitting
    标识MessageQueue是否正在关闭。

  • private boolean mBlocked
    标识MessageQueue是否阻塞。

  • private int mNextBarrierToken
    在MessageQueue中有一个概念叫消息屏障,它用于拦截同步Message,阻止这些消息被执行,只有异步Message才会放行。消息屏障本身也是一个Message,只是它的target为null并且arg1用于区分不同的消息屏障,所以该变量就是用于不断累加生成不同的消息屏障。

构造
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

接受一个参数quitAllowed,表示消息队列是否允许退出。MessageQueue目前我发现在Looper构造的时候传递的。
而构造在Looper#prepare方法中和prepareMainLooper贴上代码

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

我们默认使用的MainLooper,但是也可以在自己去调用。

  • isIdle()判断Looper是否空闲
/**
     * Returns true if the looper has no pending messages which are due to be processed.
     *
     * <p>This method is safe to call from any thread.
     *
     * @return True if the looper is idle.
     */
    public boolean isIdle() {
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            return mMessages == null || now < mMessages.when;
        }
    }
  • IdleHandler addIdleHandler removeIdleHandler

IdleHandler是一个接口,用于在线程空闲时候处理一些业务。
queueIdle是接口内部的一个方法,返回Boolean值,当消息队列中的消息都处理完毕,就会调用这个方法,返回值为true时,IdleHandler会一直保持在消息队列中,,False则会执行完该方法后移除该IdleHandler。
需要注意的是,消息队列中还有其他延迟消息时,IdleHandler也会被调用,因为线程在此时是空闲的。

/**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    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();
    }

添加一个新的IdleHandler到MessageQueue中。当IdleHandler接口的回调方法queueIdle返回false时,该IdleHandler在被执行完毕后,立即自动删除,当然也可以调用removeIdleHandler来删除指定的IdleHandler。此方法在任何线程都是线程安全的。

 /**
     * Add a new {@link IdleHandler} to this message queue.  This may be
     * removed automatically for you by returning false from
     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
     *
     * <p>This method is safe to call from any thread.
     *
     * @param handler The IdleHandler to be added.
     */
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

//从消息队列中移除一个之前添加的IdleHandler。如果该IdleHandler不存在,则不会发生任何事情。

/**
     * Remove an {@link IdleHandler} from the queue that was previously added
     * with {@link #addIdleHandler}.  If the given object is not currently
     * in the idle list, nothing is done.
     *
     * <p>This method is safe to call from any thread.
     *
     * @param handler The IdleHandler to be removed.
     */
    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }
  • isPolling
/**
     * Returns whether this looper's thread is currently polling for more work to do.
     * This is a good signal that the loop is still alive rather than being stuck
     * handling a callback.  Note that this method is intrinsically racy, since the
     * state of the loop can change before you get the result back.
     *
     * <p>This method is safe to call from any thread.
     *
     * @return True if the looper is currently polling for events.
     * @hide
     */
    public boolean isPolling() {
        synchronized (this) {
            return isPollingLocked();
        }
    }

    private boolean isPollingLocked() {
        // If the loop is quitting then it must not be idling.
        // We can assume mPtr != 0 when mQuitting is false.
        return !mQuitting && nativeIsPolling(mPtr);
    }

返回当前线程的Looper是否正在轮询,此方法是安全的。

  • addOnFileDescriptorEventListener
 /**
     * Adds a file descriptor listener to receive notification when file descriptor
     * related events occur.
     * <p>
     * If the file descriptor has already been registered, the specified events
     * and listener will replace any that were previously associated with it.
     * It is not possible to set more than one listener per file descriptor.
     * </p><p>
     * It is important to always unregister the listener when the file descriptor
     * is no longer of use.
     * </p>
     *
     * @param fd The file descriptor for which a listener will be registered.
     * @param events The set of events to receive: a combination of the
     * {@link OnFileDescriptorEventListener#EVENT_INPUT},
     * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and
     * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks.  If the requested
     * set of events is zero, then the listener is unregistered.
     * @param listener The listener to invoke when file descriptor events occur.
     *
     * @see OnFileDescriptorEventListener
     * @see #removeOnFileDescriptorEventListener
     */
    public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
            @OnFileDescriptorEventListener.Events int events,
            @NonNull OnFileDescriptorEventListener listener) {
        if (fd == null) {
            throw new IllegalArgumentException("fd must not be null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }

        synchronized (this) {
            updateOnFileDescriptorEventListenerLocked(fd, events, listener);
        }
    }

    /**
     * Removes a file descriptor listener.
     * <p>
     * This method does nothing if no listener has been registered for the
     * specified file descriptor.
     * </p>
     *
     * @param fd The file descriptor whose listener will be unregistered.
     *
     * @see OnFileDescriptorEventListener
     * @see #addOnFileDescriptorEventListener
     */
    public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
        if (fd == null) {
            throw new IllegalArgumentException("fd must not be null");
        }

        synchronized (this) {
            updateOnFileDescriptorEventListenerLocked(fd, 0, null);
        }
    }

    private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
            OnFileDescriptorEventListener listener) {
        final int fdNum = fd.getInt$();

        int index = -1;
        FileDescriptorRecord record = null;
        if (mFileDescriptorRecords != null) {
            index = mFileDescriptorRecords.indexOfKey(fdNum);
            if (index >= 0) {
                record = mFileDescriptorRecords.valueAt(index);
                if (record != null && record.mEvents == events) {
                    return;
                }
            }
        }

        if (events != 0) {
            events |= OnFileDescriptorEventListener.EVENT_ERROR;
            if (record == null) {
                if (mFileDescriptorRecords == null) {
                    mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
                }
                record = new FileDescriptorRecord(fd, events, listener);
                mFileDescriptorRecords.put(fdNum, record);
            } else {
                record.mListener = listener;
                record.mEvents = events;
                record.mSeq += 1;
            }
            nativeSetFileDescriptorEvents(mPtr, fdNum, events);
        } else if (record != null) {
            record.mEvents = 0;
            mFileDescriptorRecords.removeAt(index);
        }
    }

翻译:添加文件描述监听,当文件描述相关事件发生变化会接受到通知。
如果文件描述事件已经被注册,那么指定的Event和Listener会替换以前事件和监听。每个文件描述符不能指定多个监听。不需要文件描述监听,就把他注销掉removeOnFileDescriptorEventListener。

FileDescriptor 是“文件描述符”。
FileDescriptor 可以被用来表示开放文件、开放套接字等。
以FileDescriptor表示文件来说:当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作
FileDescriptor相关文章
http://www.jianshu.com/p/430340c4a37a
http://www.jianshu.com/p/a2dc2907ec28
http://www.jianshu.com/p/837320d6faea
http://blog.csdn.net/yuanzhangmei1/article/details/9131971

目前还不知道addOnFileDescriptorEventListener是在哪里调用的,既然FileDescriptor也是做Io操作,肯定也是对文件操作

来看看 updateOnFileDescriptorEventListenerLocked方法,从移除监听可以看出来,event 是int值,如果传入0,代表移除监听。

这个方法让我蒙圈了,不管了 ,继续往下

/**
    /**
     * Returns the int descriptor. It's highly unlikely you should be calling this. Please discuss
     * your needs with a libcore maintainer before using this method.
     * @hide internal use only
     */
    // Android-added.
    public final int getInt$() {
        return descriptor;
    }

下面这段代码

int index = -1;
        FileDescriptorRecord record = null;
        if (mFileDescriptorRecords != null) {
            index = mFileDescriptorRecords.indexOfKey(fdNum);
            if (index >= 0) {
                record = mFileDescriptorRecords.valueAt(index);
                if (record != null && record.mEvents == events) {
                    return;
                }
            }
        }

FileDescriptorRecord是存放监听,事件,文件描述符的实体类,根据getInt$的返回值作为键,去集合中找这个值,SparseArray看成是HashMap,1.如果真有且集合不为null,并且事件相同那就直接返回了。

if (events != 0) {
            events |= OnFileDescriptorEventListener.EVENT_ERROR;
            if (record == null) {
                if (mFileDescriptorRecords == null) {
                    mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
                }
                record = new FileDescriptorRecord(fd, events, listener);
                mFileDescriptorRecords.put(fdNum, record);
            } else {
                record.mListener = listener;
                record.mEvents = events;
                record.mSeq += 1;
            }
            nativeSetFileDescriptorEvents(mPtr, fdNum, events);
        } else if (record != null) {
            record.mEvents = 0;
            mFileDescriptorRecords.removeAt(index);
        }

这部分就很好理解,就是把FileDescriptorRecord对象以fdNum为键存进集合中。

  • dispatchEvents
// Called from native code.
    private int dispatchEvents(int fd, int events) {
        // Get the file descriptor record and any state that might change.
        final FileDescriptorRecord record;
        final int oldWatchedEvents;
        final OnFileDescriptorEventListener listener;
        final int seq;
        synchronized (this) {
            record = mFileDescriptorRecords.get(fd);
            if (record == null) {
                return 0; // spurious, no listener registered
            }

            oldWatchedEvents = record.mEvents;
            events &= oldWatchedEvents; // filter events based on current watched set
            if (events == 0) {
                return oldWatchedEvents; // spurious, watched events changed
            }

            listener = record.mListener;
            seq = record.mSeq;
        }

        // Invoke the listener outside of the lock.
        int newWatchedEvents = listener.onFileDescriptorEvents(
                record.mDescriptor, events);
        if (newWatchedEvents != 0) {
            newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
        }

        // Update the file descriptor record if the listener changed the set of
        // events to watch and the listener itself hasn't been updated since.
        if (newWatchedEvents != oldWatchedEvents) {
            synchronized (this) {
                int index = mFileDescriptorRecords.indexOfKey(fd);
                if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
                        && record.mSeq == seq) {
                    record.mEvents = newWatchedEvents;
                    if (newWatchedEvents == 0) {
                        mFileDescriptorRecords.removeAt(index);
                    }
                }
            }
        }

        // Return the new set of events to watch for native code to take care of.
        return newWatchedEvents;
    }

这个方法由native调用
传递2个参数,int fd int events
第一行注释代码说明:获取文件描述符记录和可能更改的任何状态。
根据int 值,从集合取出对应的实体对象,如果对象=null,则返回0,代表没有监听,把对象的数据获取出来进行赋值运算,根据当前的事件过滤,()然后执行onFileDescriptorEvents方法,获取新的events事件。接下来就判断需不需要更新这个事件。

消息屏障

消息屏障也是一个Message,与其他消息不同的是消息屏障的Target=null,when=当前毫秒值,arg1是token,用于区分不同消息屏障,消息屏障的作用是拦截同步消息队列中的同步消息,放行异步消息。(在next方法中的do while中体现,只有消息屏障Target可以为空null,其他的Target为null直接抛异常了)

  • 添加消息屏障到队列
public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        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;
        }
    }

添加消息屏障到消息队列,when是当前时间执行点。arg1的值是token,消息屏障的唯一标识,值递增。final Message msg = Message.obtain();这个msg就是消息屏障,它的Target是null。
断言when>0,p!=null,循环遍历消息队列,利用when进行比较,p.when只要小于when的都插入到队列前面.

m3 -- m2 --m1--m0
如果m3的when小于msg的when
m3 -- Barrier--m2--m1--m0
如果m3的when大于msg的when
Barrier--m3--m2--m1--m0

最后返回一个新的Token。

  • 移除消息屏障
/**
     * Removes a synchronization barrier.
     *
     * @param token The synchronization barrier token that was returned by
     * {@link #postSyncBarrier}.
     *
     * @throws IllegalStateException if the barrier was not found.
     *
     * @hide
     */
    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        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 the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

删除消息屏障比较简单,遍历消息队列匹配token,在释放资源之前排列好队列。

重要方法 enqueueMessage

boolean enqueueMessage(Message msg, long when) {
//tartget不能为null,需要知道是那个Handler发送到消息队列,也是根据这个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,返回False,告诉Handler添加消息失败,并释放资源,把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 p = mMessages;
            boolean needWake;
//如果消息队列没有Message,或者要添加的消息延时时间是0,或者需要加入到队列的Message时间节点小于链表第一个Message,都会把这个消息直接放入消息队列的头。
            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.
//把msg插入到适合的位置,根据when,msg与p比较,把p插入msg的后面,msg插入prew的前面。prew最初是mMessage。
                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;
    }

将Message插入到消息队列中,首先判断Target,其次是否在使用中,如果消息队列正在退出,则添加消息失败,释放资源。
在真正加添消息钱,把Message标记成正在使用。
消息添加到队列

  1. p==null 表示队列中没有消息,放入到列表头。
  2. when=0 表示此消息需要立即发送,直接加入到列表的头。
  3. 再此消息列表中,时间节点小于其他Message。
    满足上面其中一种情况,Message直接加入MessageQueue中。

否则:
进入死循环,遍历Message,根据when找到合适的位置进行插入。(next获取下一个Message,如果下一个Message的when大于当钱msg的when,将msg.next赋值给Message,msg.next赋值给p.next,这样就行形成一个链表)
最后判断是否唤醒线程。native方法调用。返回true表示Message加入MessageQueue成功。

  • 移除消息系列
 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;
            }
        }
    }
    //移除所有未执行的Message。
    private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

/**
     * 移除所有执行时间点在当前时间点之后的Message,而在当前时间点之前的Message会保留在消息队列中,
     * 直到它们都被处理完为止。
     */
    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);
            }
        }
    }
  • removeMessages 上半部分
while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

如果消息队列的头就有符合条件的Message,就从头开始删除符合条件的Message,并不断地更新mMessage的指向。

  • removeMessages 后半部分
// 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;
            }

到了这里前半部分没有满足,所以不会发生,不在需要关系指向问题,直接删除符合条件的Message。

从消息队列中删除Message的操作也是遍历消息队列然后删除所有符合条件的Message,但是这里有个小细节需要注意,从代码中可以看出删除Message分为两次操作,第一次是先判断符合删除条件的Message是不是从消息队列的头部就开始有了,这是时候会涉及到修改mMessages指向的问题,而mMessages代表的就是整个消息队列;在排除了第一种情况之后,剩下的就是继续遍历队列删除剩余的符合删除条件的Message。其他重载方法也是同样的操作,唯一的区别就是删除条件不同而已。

关闭消息队列

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(false)

这是一种强制关闭消息队列的方式,这种做法是不安全的方式,因为它会直接删除队列中所有未执行的Message,这一点可以从removeAllMessagesLocked()方法看出。

  • quit(true)

安全关闭消息队列的方式,从removeAllFutureMessagesLocked()可以看出,这种方式会删除消息队列中所有还没有到达执行时间点的Message,而剩下的已经达到执行时间点的Message就会被保留,直到都被执行为止。

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

        //记录空闲时处理IdleHandler的数量
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        //阻塞线程时间 具体看下面
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                //释放可是释放的资源
                Binder.flushPendingCommands();
            }
            //调用native方法来阻塞线程
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                // 需要操作的msg和需要进行比较的时间
                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());
                }
               //  判断还有没有可用的Message
                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 {
                        //  执行的时间到了
                        //重新排列Message
                        // 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.
                    //  没有Message了,可以睡觉了。
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
              // 关闭消息队列,返回null,通知Looper停止循环
                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.
                 // 第一次循环的空闲时候调用,所谓的空闲,指的是消息队列中没有Message了。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
               // 这里厉害了如果判断成立则说明没有IdleHandler和Message了,直接下次循环阻塞线程。
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                // 最小处理4个IdleHandler 具体看pendingIdleHandlerCount,如果只有2个呢?其他两个为Null?还是在其他什么地方调用,需要添加4个IdleHandler。主要还是看addIdleHandler方法在什么地方调用。
                    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.
            //开始执行所有的IdleHandler,在根据返回的keep决定是否删除。
            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.
            //idle handler都执行完了,当然要初始化了
            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.
          //在处理idle handler的时候耗费了一些时间,也许又有其他的Message到达了呢,重置变量,重新检查消息队列。
            nextPollTimeoutMillis = 0;
        }
    }

分开来写。还是写在代码上把。

final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

mPtr是从native方法中得到的NativeMessageQueue地址
如果mPtr等于0说明队列不存在或被清除掉了

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
  • pendingIdleHandlerCount
    在空闲时需要处理IdleHandler的数量,只有第一次初始化的时候才能为-1。
  • nextPollTimeoutMillis
    通知native层阻塞线程
    nextPollTimeoutMillis = -1 一直阻塞直到再次唤醒他
    nextPollTimeoutMillis = 0 不阻塞线程
    nextPollTimeoutMillis >0 阻塞线程多长时间
    既然有阻塞线程,肯定也有唤醒线程,在enqueueMessage调用nativeWake(mPtr);唤醒线程。

接下来是一个死循环:
for(;;)

if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
}

如果!=0,说明要阻塞线程了。为了长时间阻塞线程释放掉部分对象。

nativePollOnce(ptr, nextPollTimeoutMillis);
这个方法可厉害了,就是这个方法来阻塞线程的,根据指定的ptr来阻塞,nextPollTimeoutMillis在上面呢。

final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;

now以这个时间为基准开始比较,msg就是要发送给Looper的消息,主要看是如何对这两个Message进行操作的

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

目前,msg是队列的头,也是当前的队列,Target为null,只有一种情况,那就是消息屏障。if判断队列的头是不是消息屏障。

  • do while
    不断地进行轮询,同步Message直接过滤,知道获取到第一个异步消息轮询结束。msg就是那个异步的Message,prevMes.next = msg;这里可以看的出来优先处理异步消息。过滤掉的消息不是指删除掉了,只是找到异步消息发送Looper,在下次循环的时候Message msg = mMessages;只有发送的异步消息从队列移除了。
//重新排列Message
                        // 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();
                      //  把消息返回

如果prewMsg

  • == null
    执行的是同步消息,把mMessage这个消息队列重新赋值,next成了头。
  • != null
    代表执行的异步消息,也是重新排列队列。相当把异步消息移除。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350