阅读Android消息机制源码的随手笔记

Looper、Message、Handler、MessageQueue是Android消息机制的几个主要要素:

  • Looper:循环,不停的循环从MessageQueue读取消息
  • MessageQueue:消息队列,通过一个单链表数据结构来维护消息队列
  • Handler:用来发送和处理消息
  • Message:消息,包含必要的描述和属性数据

Looper部分源码

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */

这是在Looper类中的注释,新线程默认是没有关联Looper对象,所以首先需要调用prepare()创建一个Looper对象,然后调用loop()循环处理消息直到Looper执行退出操作。

prepare()和prepare(boolean quitAllowed)方法

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

在上面源码中主要关注这个成员变量sThreadLocal,这是一个ThreadLocal的实例。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal是什么?ThreadLocal是一个线程内部的数据存储类,它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,其它线程无法获取到该线程存储数据。

如果执行prepare()的线程已经有了一个Looper实例就抛出RuntimeException异常,否则创建一个Looper实例并保存到sThreadLocal中。这也是为什么一个线程只能创建一个Looper实例。

下面我们看下Looper的构造函数

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

在构造函数中,创建一个MessageQueue消息队列实例mQueue,并且保存当前线程的对象,参数quitAllowed表示该线程是否允许Looper退出循环。

loop()方法

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
  • 调用myLooper()方法返回当前线程关联的Looper对象,如果当前线程没有任何关联的Looper对象,loop()方法会抛出异常,异常信息提示我们在执行loop()方法前,需要先执行prepare()方法。
  • 执行for无限循环,在循环中调用mQueue.next()读取消息队列中的消息,当读取的消息为空时,表示消息队列正在执行退出操作,直接return终止循环。
  • 调用msg.target.dispatchMessage(msg)方法处理消息。
  • 调用msg.recycleUnchecked()方法回收消息,进入下一次循环。
  • 根据loop()方法的注释,当我们需要终止消息循环时,可以调用Looper.quit()方法。

quit()和quitSafely()方法

public void quit() {
    mQueue.quit(false);
}

public void quitSafely() {
    mQueue.quit(true);
}

这两个方法实际上都是调用MessageQueue的quit(boolean safe)方法,该方法会将消息队列标识为正在退出并移除消息队列中的消息,导致loop()方法中读取的消息为空终止循环。
这两个方法的区别,我们等看到MeaageQueue的quit(boolean safe)方法源码时在来分析。

除了上述方法外,Looper还提供了一些其他的方法。

prepareMainLooper()方法

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

通过方法注释,可以知道调用该方法创建的Looper对象,会被当做应用程序主线程的Looper对象。Android系统会调用该方法为我们创建主线程的Looper,我们不需要自己手动去调用。
与prepare()方法不同该方法传递的quitAllowed参数为false,表示该线程的Looper.loop()方法不能被终止,即主线程的消息循环不允许被终止。

getMainLooper()方法

/**
 * Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

返回主线程的Looper对象。

isCurrentThread()方法

/**
 * Returns true if the current thread is this looper's thread.
 */
public boolean isCurrentThread() {
    return Thread.currentThread() == mThread;
}

判断当前线程是否是创建Looper对象的线程。

MessageQueue部分源码

MessageQueue消息队列,主要包含2个操作:插入和读取。插入和读取对应的方法分别为enqueueMessage(Message msg, long when)next()
虽然MessageQueue叫消息队列,但是它实际上它是通过一个单链表的数据结构来维护消息列表,单链表在插入和删除上比较有优势。

enqueueMessage(Message msg, long when)方法

boolean enqueueMessage(Message msg, long when) {
    // 如果msg没有指明一个用来处理它的Handler则抛出异常
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    // 如果msg已经被标识为使用中则抛出异常
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    // 同步锁
    synchronized (this) {
        // 如果MessageQueue正在退出则抛出异常,并将msg回收
        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;
        // 如果当前MessageQueue的链表头结点为空,或者msg触发时间为0,或者msg的触发时间
        // 小于头结点的触发时间,则将msg插入到链表头部作为整个MessageQueue的头结点
        // 同时线程如果是阻塞的,把needWake设为true
        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 {
            // 插入msg到链表的中间
            // 如果线程阻塞、链表头结点是同步屏障消息、msg是异步消息,把needWake设为true
            // 循环消息链表,如果链表节点为空或者节点触发时间长于msg,则将msg插入到该链表
            // 节点前面
            // 如果在msg之前已经有异步消息且needWake已经标识为true,将needWake设为false

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

        // 根据needWake标识判断是否需要唤醒线程处理
        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

从上面的源码来看该方法主要是将消息插入到单链表中的合适位置,并判断是否需要唤醒线程。

next()方法

我们在前面介绍Looper源码时了解到,该方法会在Looper.loop()方法中反复被调用。

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它保存着对应的Native的消息队列实例的地址
    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();
        }

        // nextPollTimeoutMillis >= 0 表示距离该消息处理时间的总时长
        // 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) {
                // 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) {
                // 如果当前还没到待处理消息的触发时间,设置激活等待时间,否则处理这个消
                // 息,将MessageQueue设置为非blocked状态,并将消息从链表中移除,然后为
                // 消息设置FLAG_IN_USE的标识并返回该消息
                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 {
                // 没有消息,将nextPollTimeoutMillis 设置为-1
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // 如果当前没有待处理消息或者还没到待处理消息触发时间并且MessageQueue要求
            // 退出,则销毁并返回null
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // 在第一次进入for循环且当前没有消息需要处理,获取其他待处理事务的数量
            // 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();
            }
            // 如果没有其他利用队列空闲要处理的事务,则将MessageQueue设置为blocked,
            // 进入下次循环
            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);
                }
            }
        }

        // 设置pendingIdleHandlerCount为0,以后不需要在处理这些事务
        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // 设置nextPollTimeoutMillis为0,因为当我们在处理其他事务的时候,新的Message
        // 可能已经到来了,所以我们不需要等待,立即开始下次循环来检查
        // 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;
    }
}

通过上面源码我们知道,该方法会不停地去循环读取MessageQueue中待处理的消息。
如果当前MessageQueue中没有消息,该方法会暂时阻塞等待消息的到来,从而导致Looper.loop()方法也阻塞。如果这时发送一条消息会唤醒线程获取该消息。
当该方法读取到待处理消息,如果待处理消息的触发时间长于当前时间就设置合理的等待时间,否则返回该消息,并将其从单链表中移除。

postSyncBarrier()方法和removeSyncBarrier(int token)方法

在上面next()方法中,获取消息时检查了链表头结点是否是同步消息屏障,那什么是同步消息屏障?
同步消息屏障(SyncBarrier)是一个特殊的Message,它的targer为null。当消息队列遍历到这种消息类型的时候,它会跳过后面的同步Message获取最先要处理的异步Message。
我们可以调用postSyncBarrier()方法向单链表中插入一条SyncBarrier

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在单链表的合适位置插入一条target == null的消息,并返回一个token。

调用removeSyncBarrier(int token)方法从单链表中移除SyncBarrier

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

根据postSyncBarrier(long when)方法返回的token,从单链表中移除并回收指定token的SyncBarrier,如果该消息位于链表的头结点,则将下个节点设为头结点,若该头结点消息不是一个SyncBarrier并且MessageQueue不要求退出,唤醒线程。

  • SyncBarrier可以用来预加载网络请求、预加载图片、预加载文件、读取数据库等。另外,为了让View能够有快速的布局和绘制,ViewRootImpl在执行measure()和draw()时,会向主线程的MessageQueue添加SyncBarrier。
  • 由于SyncBarrier会屏蔽后续的同步消息,所以当执行完任务后要记得把SyncBarrier移除消息队列。

removeMessages和removeCallbacksAndMessages方法

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方法只是传递的参数不同,其方法逻辑都是相同的,即从链表头结点开始依次移除并回收所有匹配的消息。

quit(boolean safe)方法

在Looper源码中我们知道,通过调用Looper.quit()和Looper.quitSafely()方法都可以退出循环,而这两个方法都是执行的该方法。

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);
    }
}
  1. 当mQuitAllowed为false会抛出异常,从异常信息中得知,主线程不允许退出消息循环。这点可以在之前的Looper.prepareMainLooper()方法中提及过。
  2. 如果当前MessageQueue已经被标识为正在退出直接return,否则就标识为正在退出。此时如果往消息队列中插入消息将会直接回收该消息并返回false表示消息发送失败。
  3. 在方法中根据参数safe,执行不同的消息移除方法。(removeAllFutureMessagesLocked()和removeAllMessagesLocked())
  4. 执行nativeWake(mPtr)方法唤醒线程。
- removeAllMessagesLocked()

Looper.quit()退出循环前的清空消息实际上调用的方法。

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

依次移除并回收MessageQueue中的所有消息。

- removeAllFutureMessagesLocked()

Looper.quitSafely()退出循环前的清空消息实际上调用的方法。

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

与removeAllMessagesLocked()方法不同,该方法只会依次移除并回收MessageQueue中所有还没到触发时间的消息。

如果想了解关于Native looper的知识,可以看看这篇博客

Message部分源码

Message是线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。常用属性:arg1、arg2、what、obj、target等,其中arg1和arg2可以存放整型数据,what可以用来标识一条Message,obj可以存放Object类型的任意对象,target就是处理一个Message的Handler。

虽然Message的构造函数是public的,但是最好是使用Message.obtain()方法获取Message对象,因为该方法的实现中包含了回收再利用的机制,可以提供效率。

在了解obtain()方法和recycle()方法之前,我们需要先知道sPoolnextsPoolSize这些指的是什么。

/*package*/ Message next;
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;

多个Message通过单链表结构组合成一个默认最大长度为50的消息池,sPool是消息池中的第一个对象即头结点,sPoolSize是消息池中当前的消息数量,next是当前Message结点的下一个结点。

obtain()方法

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

如果消息池中没有消息,我们直接调用Message的构造方法创建一个新的Message对象,否则从消息池中读取头结点消息并把该消息的下一个消息设为头结点,然后将消息从消息池中移除并清除所有标识(FLAG_IN_USE和FLAG_ASYNCHRONOUS)。

recycle()和recycleUnchecked()方法

public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

在recycleUnchecked()方法中,首先把消息标识为使用中,再将其他的属性重置初始化,最后如果消息池还没满就将消息插入到消息池的头结点位置。
在recycle()方法中会先判断消息是否有使用中的标识,如果没有才会调用recycleUnchecked()方法。

另外,在Message中还有一些其他的方法比如

getData()和peekData()

public Bundle getData() {
    if (data == null) {
        data = new Bundle();
    }
        
    return data;
}

public Bundle peekData() {
    return data;
}

这两个方法都是返回data数据,区别是当data == null时,peekData()会返回null,而getData()会创建并返回一个Bundle对象。

setTarget(Handler target)和sendToTarget()

public void setTarget(Handler target) {
    this.target = target;
}

public void sendToTarget() {
    target.sendMessage(this);
}

setTarget(Handler target)设置发送和处理消息的handler,sendToTarget()利用handler发送消息。

setAsynchronous(async)

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}

是否设置成异步消息。

Handler部分源码

构造方法

Handler类虽然有多个不同参数的构造方法,但最终调用的还是下面这两个构造方法。

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

从上面源码中我们可以出来这两个构造方法实际上都是为mLoopermQueuemCallbackmAsynchronous这4个成员变量赋值,不同的是如果构造方法参数中没有Looper对象,需要调用Looper.myLooper()方法获取当前线程中的Looper对象。其中mQueue是用来接收Handler对象所发送消息的消息队列,mLooper所在的线程就是Handler对象处理消息的线程。

obtainMessage方法

public final Message obtainMessage()
{
    return Message.obtain(this);
}

public final Message obtainMessage(int what)
{
    return Message.obtain(this, what);
}
    
public final Message obtainMessage(int what, Object obj)
{
    return Message.obtain(this, what, obj);
}

public final Message obtainMessage(int what, int arg1, int arg2)
{
    return Message.obtain(this, what, arg1, arg2);
}
    
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
    return Message.obtain(this, what, arg1, arg2, obj);
}

obtainMessage方法实际上都是调用相应Message的obtain()方法创建消息。

enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法

Handler类中所有的post系列方法,实际上都是调用相应的sendMessage方法,而所有的sendMessage系列方法最后都是调用该方法往消息队列中插入消息。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

该方法最后调用queue.enqueueMessage(msg, uptimeMillis)方法往mQueue中插入消息。

-sendMessageAtFrontOfQueue(Message msg)方法

从方法名我们可以看出该sendMessage方法是将消息插入到消息队列的队首,我们看下源码是不是这样。

public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
             this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, 0);
}

方法最后调用enqueueMessage(queue, msg, 0)方法,我们在MessageQueue的enqueueMessage方法源码中分析过,当when == 0会将消息插入到链表的头结点。所以调用该方法发送的消息会插入到消息队列的队首。

dispatchMessage(msg)方法

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
  1. 调用消息内部的处理方法,如果没有执行2
  2. 调用创建Handler对象时指定的处理方法,如果没有执行3
  3. 调用Handler对象自身的处理方法

runWithScissors(final Runnable r, long timeout)方法

这个方法将会在Handler所在的线程中执行传入的Runnable对象,同时阻塞调用线程的执行,直到Runnable对象的run()方法执行完毕。

public final boolean runWithScissors(final Runnable r, long timeout) {
    if (r == null) {
        throw new IllegalArgumentException("runnable must not be null");
    }
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout must be non-negative");
    }

    if (Looper.myLooper() == mLooper) {
        r.run();
        return true;
    }

    BlockingRunnable br = new BlockingRunnable(r);
    return br.postAndWait(this, timeout);
}

private static final class BlockingRunnable implements Runnable {
    private final Runnable mTask;
    private boolean mDone;

    public BlockingRunnable(Runnable task) {
        mTask = task;
    }

    @Override
    public void run() {
        try {
            mTask.run();
        } finally {
            synchronized (this) {
                mDone = true;
                notifyAll();
            }
        }
    }

    public boolean postAndWait(Handler handler, long timeout) {
        if (!handler.post(this)) {
            return false;
        }

        synchronized (this) {
            if (timeout > 0) {
                final long expirationTime = SystemClock.uptimeMillis() + timeout;
                while (!mDone) {
                    long delay = expirationTime - SystemClock.uptimeMillis();
                    if (delay <= 0) {
                        return false; // timeout
                    }
                    try {
                        wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            } else {
                while (!mDone) {
                    try {
                        wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }
        return true;
    }
}

下面我是根据方法上的注释结合方法源码的大致翻译

  • 如果当前线程和handler所在线程是同一线程,直接执行run()方法。否则将runnable发送到handler相应的消息队列中并同步等待其run()方法运行完毕。
  • 该方法是危险的,使用不当可能导致死锁(因为同步等待就是靠锁实现的)。
    永远不要在持有任何锁时或者在重入的操作中调用此方法。
  • 该方法可能在这种情景中用到:一个后台线程需要同步的等待handler所在线程的一个task执行完毕。不过这往往是不良设计的一个征兆,这个时候可能改进程序设计更合适。
  • 一种更合适的方法:你只需要启动一个Handler线程,然后在执行后续操作之前,将一些初始化的操作交给Handler线程来执行。
  • 如果超时的话,该方法会返回false。但runnable依然在消息队列中,稍后run()方法有可能会被执行。
  • 如果使用了这个方法,那么在结束循环的时候一定要调用Looper.quitSafely(),否则会造成这个方法的永久挂起。
  • @hide隐藏方法。这个方法容易被滥用应该会从API中拿掉。
    即使要把它当成API的一部分,我们也会先把它重命名成runUnsafe()类似这样的名称。

ActivityThread类和HandlerThread类

-ActivityThread

我们在创建主线程handler的时候,通常都是直接调用Handler()构造方法,但在创建handler之前主线程需要先有一个Looper对象,在前面的Looper.prepareMainLooper()方法中我们提及过Android系统会调用该方法为我们创建主线程的looper,我们不需要自己手动去调用,但并不清楚Android系统是什么时候在主线程中创建looper的。

ActivityThread类管理应用进程的主线程的执行,ActivityThread中的main()方法相当于普通Java程序的main()方法就是作为Android程序的入口,主线程的Looper对象就是在这里创建的。

public static void main(String[] args) {

    // ...

    Looper.prepareMainLooper();

    // ...

    Looper.loop();

    // ...
}

方法中先执行Looper.prepareMainLooper()方法创建looper,然后再执行Looper.loop()方法启动消息循环。

-HandlerThread

如果我们想让主线程通知子线程执行一些任务时,我们可以在主线程将要通知的子线程中的Looper对象当做参数调用Handler(looper)构造方法创建handler,然后在主线程中调用handler发送消息到子线程的消息队列中,通知子线程执行任务。

HandlerThread是一个线程类继承了Thread,它有自己的内部Looper对象,可以进行消息循环。

public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
        
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

我们可以先在主线程中创建启动一个HandlerThread线程,然后调用HandlerThread线程的getLooper()方法获取looper,再在主线程中调用Handler(looper)创建handler,handler会往HandlerThread线程的消息队列中发送消息,并在该线程中处理消息。
此外,因为Handler对象是在主线程中创建的,所以getLooper()方法是在主线程中调用的,而Looper.myLooper()方法是在HandlerThread线程中执行的,因此主线程需要等待HandlerThread线程的Looper.myLooper()执行完毕后才返回Looper对象。

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

推荐阅读更多精彩内容