万字解析Android Handler实现原理

前言

Handler 部分的讲解计划分两篇博客讲完实现原理,一篇主要介绍 Java 层的实现,另一篇介绍 Native 相关的实现,本篇介绍前者。

讲解完实现原理之后,会再新开几篇博客讲解其在系统源码中的主要使用场景等相关内容。

本篇主要内容有:

  1. MessageQueue 的基本运作流程,包括入队、出队、消息处理等实现原理
  2. 同步屏障和异步消息
  3. IdleHandler
  4. Message池管理

如果这篇文章对你有帮助,请关注点赞加收藏。

还可以关注我的微信公众号"ZZH的Android",还有更多 Android 系统源码解析的干货文章等着你。

1、Handler 作用

(1) 在线程之间互发消息,实现线程间通信

(2) 发送延时消息,延时执行任务

2、类图

3、Handler-Looper-MessageQueue 模型

整个模型的运行步骤如下:

  1. 创建 MessageQueue 队列
  2. 将 Message 对象入队
  3. 将 Message 对象出队
  4. 处理出队的 Message 后面跟代码流程时,可以跟着这个图来看,方便理解。

以上运行步骤依赖于 Handler、Looper 和 MessageQueue 三个核心类实现。

4、实现原理

在 Looper.java 文件的开头,给了一段 Looper 使用的示例代码,如下

java 复制代码
class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
       // 创建MessageQueue对象
       Looper.prepare();

       mHandler = new Handler(Looper.myLooper()) {
           public void handleMessage(Message msg) {
               // process incoming messages here
           }
       };
       // 启动MessageQueue的循环运转
       Looper.loop();
    }
}

其中,Looper.prepare()创建 MessageQueue 对象;

Looper.loop()启动 MessageQueue 队列的循环运转;

Handler 负责发送消息和处理消息。

此处需要注意的是,子线程中并不是必须要创建消息处理机制的这几个对象的,只有当有业务需要才创建即可。

主线程必须创建,而且是由系统在进程启动时创建的,不需要 App 开发者再手动创建。

Looper 给主线程单独定义了专门的方法 prepareMainLooper,代码位置如下:

java 复制代码
// frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
    ......
    // 创建主线程的MessageQueue
    Looper.prepareMainLooper();

    ......

    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    // 启动MessageQueue循环运转
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

4.1 Looper.prepare()->MessageQueue 的创建

当 Looper 执行其 prepare()函数时会创建 MessageQueue 对象。MessageQueue 是一个包含 Message 元素的队列, 从其名字翻译为队列,实际上是一个单向链表,因为每个 Message 对象中的 next 成员会指向下一个 Message 对象。

创建 MessageQueue 代码的如下:

java 复制代码
// frameworks/base/core/java/android/os/Looper.java
public static void prepare() {
    // 这里的true表示MessageQueue队列可以手动停止运转。
    // 调用Looper的quit方法可以停止运转。
    prepare(true);
}

// 注意这个函数是private方法,App无法使用。说明子线程的
// quitAllowed一定为true。
// 这里使用了ThreadLocal,表明每个线程都有专属的一个唯一的Looper对象
private static void prepare(boolean quitAllowed) {
    // 如果重复执行prepare方法,就会提示错误。
    // 说明每个线程只能创建一个Looper对象
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }

    // 创建Looper对象,并保存到sThreadLocal里,
    // 这里quitAllowed=true。
    sThreadLocal.set(new Looper(quitAllowed));
}

可以通过 Looper 的 myLooper()函数获取当前线程的 Looper 对象:

java 复制代码
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

再看下给主线程使用的 prepareMainLooper 函数:

java 复制代码
// 这个方法不允许应用再调用
public static void prepareMainLooper() {
    // 这里传入了false,说明主线程的消息循环不允许被停止。
    // 原因是主线程中有很多核心的功能是由Handler实现的,
    // 比如Activity生命周期的回调。
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // 主线程的Looper对象也会保存在sMainLooper中,
        // 这样的获取,获取主线程的Looper对象就不用再从
        // sThreadLocal 中拿了,可以节省时间,提高性能。

        // 当需要创建基于主线程的Handler对象时,可以直接
        // 使用Looper的getMainLooper方法获取主线程Looper对象。
        sMainLooper = myLooper();
    }
}

Looper 的构造函数

java 复制代码
// 创建MessageQueue对象,保存在mQueue变量中;
// 并且将当前线程保存在mThread变量中。
// 这里可以看到quitAllowed最终传递给了MessageQueue
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

下面看下 MessageQueue 的构造函数

java 复制代码
// frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue(boolean quitAllowed) {
    // 是否可以被手动停止
    // 主线程必须为false
    // 子线程默认为true,也只能为true
    mQuitAllowed = quitAllowed;
    // 创建NativeMessageQueue对象,并保存其引用。
    mPtr = nativeInit();
}

下面看下 nativeInit()的实现。

c++ 复制代码
// frameworks/base/core/jni/android_os_MessageQueue.cpp
// 可以看到nativeInit()对应实现为android_os_MessageQueue_nativeInit
static const JNINativeMethod gMessageQueueMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
    ......
};

// 看下android_os_MessageQueue_nativeInit实现
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    // 创建NativeMessageQueue对象
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    // 增加强引用计数,确保该对象不会被释放
    nativeMessageQueue->incStrong(env);
    // 将nativeMessageQueue的指针返回到java层。
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

// 下面再看下NativeMessageQueue的构造函数,
// 创建了Native的Looper对象。
// Native Handler相关的另起一篇文章单独介绍
NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

再看下 Looper.quit 方法,这个方法是用来结束消息队列处理流程的。

可以看到有两个方法,最终都调用了 mQueue.quit,传递的参数不同而已。

java 复制代码
public void quit() {
    mQueue.quit(false);
}

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

MessageQueue 的 quit

java 复制代码
void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        // 将mQuitting为true,这个变量在MessageQueue循环处理消息时
        // 会做判断,如果是true,则停止 循环处理。
        mQuitting = true;

        // 参数的不同,会分别调用下面的两个方法。
        // 这两个方法涉及到队列的操作,后面我们讲完入队和出队后
        // 再分析下面两个函数的实现。
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        // 唤醒线程,这样就可以循环起来然后拿到mQuitting做判断退出。
        nativeWake(mPtr);
    }
}

小结

  1. Looper 的静态方法 prepare 函数里面创建了 Looper 对象,并且将 Looper 对象保存在了 sThreadLocal 中,这样可以保证每个线程都有一个自己的 Looper 对象。
  2. 主线程有特殊的创建 Looper 对象的方法 prepareMainLooper,并且创建完成后除了保存在 sThreadLocal 中外,还保存在了一个静态变量 sMainLooper 中。
  3. Looper 有个 MessageQueue 类型成员 mQueue,在 Looper 的构造函数中进行创建,最终创建了一个 Native 对象 NativeMessageQueue,并且 java 层拿到了它的指针。
  4. Looper 的 quit 和 quitSafely 方法可以停止消息循环,其最终调用的都是 MessageQueue 的 quit 方法。
  5. MessageQueue 有个成员 mQuitAllowed,由 Looper 的私有方法 prepare 传入,表示消息循环是否可以通过调用 quit 被停止。子线程可以被停止,主线程不能被停止。

可以推测出的结论

  1. 每个线程最多只有一个 Looper 对象和一个 MessageQueue 对象。

上面讲完 MessageQueue 队列的创建,下面开始讲入队和出队。

4.2 MessageQueue 入队操作

入队操作由 Handler 完成。

4.2.1 Handler 的创建

java 复制代码
// frameworks/base/core/java/android/os/Handler.java
/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 *
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 *
 * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs
 *   where operations are silently lost (if the Handler is not expecting new tasks and quits),
 *   crashes (if a handler is sometimes created on a thread without a Looper active), or race
 *   conditions, where the thread a handler is associated with is not what the author
 *   anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
 *   explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
 *   similar. If the implicit thread local behavior is required for compatibility, use
 *   {@code new Handler(Looper.myLooper())} to make it clear to readers.
 *
 */
@Deprecated
public Handler() {
    this(null, false);
}

以上不带参数的默认构造函数已经被弃用,注释里写了一堆原因,总结就是这么用会带来很多问题。如果要用,就显式指定其依赖的 Looper 对象。

java 复制代码
/**
 * Use the provided {@link Looper} instead of the default one.
 *
 * @param looper The looper, must not be null.
 */
public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}

/**
 * Use the provided {@link Looper} instead of the default one and take a callback
 * interface in which to handle messages.
 *
 * @param looper The looper, must not be null.
 * @param callback The callback interface in which to handle messages, or null.
 */
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}

上面两个构造函数最终都会到下面的 hide 方法。

java 复制代码
/**
 * @hide
 */
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    // 下面三个主要变量赋值,比较好理解
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    //是否为异步消息
    mAsynchronous = async;
}
  1. 第一个 Looper 参数是 NonNull,通过 Looper.myLooper()方法获取,如果没有调用过 Looper 的 prepare 方法,则会报错。
  2. 第二个参数 Callback 是 Handler 的内部接口,如果传入了这个参数不是空,则消息出队后将不会执行 Handler 的 handleMessage 方法,而是会执行实现了 Callback 接口对象的 handleMessage 方法。这样的话就不需要重写 Handler 的 handleMessage 了。
java 复制代码
/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 */
public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    boolean handleMessage(@NonNull Message msg);
}
  1. 第三个参数表示,由这个 Handler 发送的消息是否为异步消息。

异步消息

上面 Handler 的构造函数中,其中最后一个参数需要特别注意一下。

它表示由这个 Handler 发送的 Message 消息是否为异步消息。

如果参数是 false,则表示此 Handler 发送的消息都是同步消息;如果参数是 true,则表示此 Handler 发送的消息都是异步消息。

从上面 Handler 的构造函数可以看出,普通应用的话这个参数默认为 false,也就是只能创建同步消息。

对于系统进程,则可以直接调用,指定 Handler 发送的消息是同步还是异步消息。

这里第一次出现异步消息这个概念,我们现在先理解为异步消息会比同步消息优先执行。

普通应用如何创建异步消息的 Handler

那么普通应用有办法创建异步消息的 Handler 吗?答案是有。

Handler 提供了两个静态方法,如下: 可以看到其最后一个参数为 true,表示异步。

java 复制代码
@NonNull
public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null, true);
}

@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    if (callback == null) throw new NullPointerException("callback must not be null");
    return new Handler(looper, callback, true);
}

关于异步消息我们先讲这么多,后面在用到的时候会继续讲解。

4.2.2 Handler 发送 Message 入队

Handler 有一系列的 postXXX 和 sendXXX 函数,最终都会调用到 sendMessageAtTime 函数。

不同的方法给下面的函数的两个参数赋值不同而已。

uptimeMillis:表示消息的延时时间,如果函数不带 Delay 或者要插入队头的则赋值为 0;

msg: Message 对象,调用不同的 post 或者 send 方法,对其不同成员进行赋值。

java 复制代码
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    // mQueue是构造Handler时由传入的Looper对象提供。
    MessageQueue queue = mQueue;
    // 如果没有MessageQueue对象,直接报异常退出。
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // 继续执行Message入队操作
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    // Message类中有个target成员,其类型为Handler,
    // 这里将其赋值为当前Handler对象,后续消息出队后
    // 可能交给Handler处理消息。
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    // 是否为异步消息,这里可以看到,如果构造Handler时mAsynchronous为true,
    // 则将Message设置为异步标志。这样的话使用这个Handler发送的Message将
    // 都是异步Message。
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 调用MessageQueue的enqueueMessage方法入队
    return queue.enqueueMessage(msg, uptimeMillis);
}

上面使用到了 Message 的 setAsynchronous 方法,我们看下其实现:

java 复制代码
// frameworks/base/core/java/android/os/Message.java
public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}

同时 Message 还提供了一个判断自己是否为异步消息点方法:

java 复制代码
// frameworks/base/core/java/android/os/Message.java
public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}

FLAG_ASYNCHRONOUS 的值:

java 复制代码
// frameworks/base/core/java/android/os/Message.java
static final int FLAG_ASYNCHRONOUS = 1 << 1;

消息入队

这里要敲黑板了,第一个重点部分来了。

MessageQueue 的入队操作,其实就是按照 Message 的延时执行时间参数 when 来进行排序入队。

java 复制代码
// frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    // 通过Handler进行入队的话这个参数是不会为空的。
    // 这里要注意一下,并不是所有的在队列中的msg的target
    // 都不是null,有一类Message叫做同步屏障,它的target就必须为null,
    // 只不过它不是通过这个方法进行入队的。所以后面我们看到代码还有
    // msg.target == null的判断时不要惊讶。至于什么是同步屏障,
    // 后面会讲到的。
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    // 入队一个Message时加锁
    synchronized (this) {
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        // 如果此时调用了Looper的quit函数,则说明取消了MessageQueue队列的运转,直接退出。
        // 这里说明,当quit后,将不能再入队新的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
            msg.recycle();
            return false;
        }

        // 标记为正在使用状态
        msg.markInUse();
        // when就是此msg延时多久执行
        msg.when = when;
        // mMessage初始为空,表示的是队列的队头
        Message p = mMessages;
        // 当消息入队但是还未到其执行时间时,线程会进入阻塞,
        // 这里的needWake用来判断是否需要唤醒线程进行出队
        boolean needWake;
        // 这里的三个条件满足其一都会将当前Message插入队头
        // p == null,说明当前队列为空,新来的Message自然作为队头
        // when = 0,说明此Message没有延时需要立即执行,所以也放在队头
        // when < p.when,说明当前Message的延时小于队头Message的延时,所以放在队头
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            // 这里的mBlocked表示MessageQueue的出队函数next()是否阻塞。
            // 如果是阻塞状态,则队头Message变化时要将其唤醒,表明这个时候
            // 可能有消息需要出队处理了,特别是当when == 0 的时候。
            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.
            // 这里的p实际上是队头的Message,但是这里对其target == null做了判断,
            // 但是该函数的开头却有target == null的话会直接抛异常退出,那么这里会是null吗?
            // 除非有一种可能,那就是这个target为null的Message不是通过本方法入队的。
            // 这里又引入了一个新的概念:同步屏障(synchronization barriers),
            // 同步屏障我们先理解为一个target为null的Message,下面会详细介绍。

            // 如果队头是一个同步屏障且当前入队的消息是异步消息,且mBlocked为true,则
            // 需要唤醒当前线程来。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            // 下面的操作是真正入队的操作,从队头开始遍历
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                // p == null 表示遍历到了队尾
                // when < p.when 表示队列按延时时间排序
                // 延时时间越小的越靠前,越靠近队头
                if (p == null || when < p.when) {
                    break;
                }
                // 如果队列中有异步消息,则将needWake置为false。
                //
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 将当前msg插入队列,插入p的前面
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        // 如果needWake为true,则唤醒线程
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

同步屏障

上面我们又遇见了一个新的概念:同步屏障,或者叫做同步屏障机制。在这个机制中,包含一个 target 为 null 的 Message,这个 Message 并不是通过上面的 enqueueMessage 方法入队的,而是另有他法。

插入同步屏障 Message 的方法 MessageQueue 的 postSyncBarrier

java 复制代码
// frameworks/base/core/java/android/os/MessageQueue.java
/**
 * Posts a synchronization barrier to the Looper's message queue.
 * @hide
 */
@UnsupportedAppUsage
@TestApi
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) {
        // mNextBarrierToken 表示每个同步屏障Message的token,
        // 初始值为0,从1开始记录
        final int token = mNextBarrierToken++;
        // 这里队Message赋值,可以看到并没有给target赋值,所以msg.targt=null
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        // 将同步屏障msg入队,这里按照when的大小排序入队
        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插到p的前面
            msg.next = p;
            prev.next = msg;
        } else {
            // msg插入队头的情况
            msg.next = p;
            mMessages = msg;
        }
        // 返回值为msg的token
        return token;
    }
}

移除同步屏障 Message 的方法 removeSyncBarrier(int token), 参数为 postSyncBarrier 时返回的 token 值。

java 复制代码
/**
 * 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
 */
@UnsupportedAppUsage
@TestApi
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;
        }
        // 如果p为空,说明没找到需要移除的同步屏障Message
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        // 如果p != null,则说明p就是需要移除的msg
        final boolean needWake;
        if (prev != null) {
            // 要移除的msg不是队头
            prev.next = p.next;
            needWake = false;
        } else {
            // 要移除的msg是队头
            mMessages = p.next;
            // 队头发生变更,且队头是同步消息,needWake为true
            needWake = mMessages == null || mMessages.target != null;
        }
        // 释放移除掉的Message p
        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);
        }
    }
}

到目前为止,我们已经接触并分别简单介绍了同步屏障和异步消息两个概念了。

他们是两个 Message 对象,同步屏障的特点是 msg.target=null,异步消息的特点是 isAsynchronous 为 true。

那么这两者的作用究竟是什么呢?其实他们两个是一起发生作用的,或者说,同步屏障的作用是为了保证异步消息可以优先执行。当出队遇到队头为同步屏障时,则其后面的同步消息将不会被执行,只会执行异步消息,除非将同步屏障移除。

需要说明的是,添加和移除同步屏障是 hide 方法,只能系统进程使用。

下面的出队操作将会看到同步屏障和异步消息的处理逻辑和其作用。

4.3 MessageQueue 的流转-出队

4.3.1 Looper.loop()

Looper.loop()方法执行后消息队列开始运转,按规则进行出队操作。

java 复制代码
/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
@SuppressWarnings("AndroidFrameworkBinderIdentity")
public static void loop() {
    // 对Looper进行检查
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }

    // loop开始后设置mInLoop为true
    me.mInLoop = true;

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

    // Allow overriding a threshold with a system prop. e.g.
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    // 这里可以通过属性设置一个消息处理时间的阈值,如果消息处理超过这个时间,将会
    // 有警告级别日志打印,默认这个属性是没有的,调试的时候可以自己设置这个属性来
    // 测试消息处理时间。
    // Looper是有专门的接口设置这个时间阈值的,如果同时设置了这个属性值,那么Looper接口
    // 设置的值会被这个属性值覆盖,后面代码可以看到。
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    // 这个值默认为false,表示msg的投递时间未超过设置的阈值
    me.mSlowDeliveryDetected = false;

    // 这里进入了for循环,如果loopOnce返回false,则会退出循环,
    // 也就表示MessageQueue不工作了,除非再次调用loop方法,
    // 当调用quit方法后,loopOnce将会返回false
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

下面看下 loopOnce 方法的实现

java 复制代码
/**
 * Poll and deliver single message, return true if the outer loop should continue.
 */
@SuppressWarnings("AndroidFrameworkBinderIdentity")
private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    // 执行MessageQueue的next()函数,返回下一个将要处理的Message,
    // 如果下一个Message时间未到,next()方法则会阻塞等待。
    // 这个函数下面会详细分析。
    Message msg = me.mQueue.next(); // might block
    // 如果调用了Looper的quit方法,那么next()函数将会返回null。
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return false;
    }

    // This must be in a local variable, in case a UI event sets the logger
    // 这里是打印Message的处理日志的,这里的mLogging可以通过Looper的
    // setMessageLogging方法设置,默认为null。
    final Printer logging = me.mLogging;
    if (logging != null) {
        logging.println(">>>>> Dispatching to " + msg.target + " "
                + msg.callback + ": " + msg.what);
    }
    // Make sure the observer won't change while processing a transaction.
    // sObserver可以通过Looper的setObserver方法设置,是用来监听回调
    // Message的处理状态的。
    final Observer observer = sObserver;

    // Looper的setTraceTag方法可以设置mTraceTag
    // 用来记录trace信息。此方法为hide方法,普通应用无法使用。
    final long traceTag = me.mTraceTag;
    // Looper的setSlowLogThresholdMs方法用来设置这两个值,也是hide方法。
    // 到这里需要解释一下这两个值的含义了。
    // mSlowDispatchThresholdMs: 消息时间的阈值,或者理解为处理完一个消息的时间阈值。
    // 如果消息处理时间比这个大,说明消息处理的比预期慢了,可能发生了异常。
    // mSlowDeliveryThresholdMs: 表示消息投递的时间差阈值,比如我的Message是延时500毫秒
    // 执行的,实际开始处理的时间(后面的dispatchStart)是550毫秒后,这就差了50毫秒,如果我们
    // 设置的阈值小于50,就说明消息投递的时间误差超出预期,可能发生异常。
    long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
    long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
    // thresholdOverride前面我们讲过,是调试时需要手动设置的属性值。
    // 如果这个值大于0,则覆盖setSlowLogThresholdMs方法设置的两个值。
    if (thresholdOverride > 0) {
        slowDispatchThresholdMs = thresholdOverride;
        slowDeliveryThresholdMs = thresholdOverride;
    }
    // 是否打印警告日志的开关
    final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
    final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

    final boolean needStartTime = logSlowDelivery || logSlowDispatch;
    final boolean needEndTime = logSlowDispatch;

    // 记录开始处理msg的trace
    if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
        Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
    }

    // 记录消息派发的开始时间
    final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
    final long dispatchEnd;
    Object token = null;
    if (observer != null) {
        // observer回调开始派发消息
        token = observer.messageDispatchStarting();
    }
    long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
    try {
        // 执行Handler的dispatchMessage函数
        msg.target.dispatchMessage(msg);
        if (observer != null) {
            // observer回调派发消息结束
            observer.messageDispatched(token, msg);
        }
        // 记录消息派发的结束时间
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } catch (Exception exception) {
        if (observer != null) {
            // observer回调派发消息发生异常
            observer.dispatchingThrewException(token, msg, exception);
        }
        throw exception;
    } finally {
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            // 记录处理完成msg的trace
            Trace.traceEnd(traceTag);
        }
    }
    if (logSlowDelivery) {
        // 前面说过,mSlowDeliveryDetected初始化为false
        if (me.mSlowDeliveryDetected) {
            if ((dispatchStart - msg.when) <= 10) {
                Slog.w(TAG, "Drained");
                me.mSlowDeliveryDetected = false;
            }
        } else {
            // showSlowLog函数用来计算dispatchStart-msg.when是否大于slowDeliveryThresholdMs,
            // 如果是,则打印警告日志。
            if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                    msg)) {
                // Once we write a slow delivery log, suppress until the queue drains.
                // 并且将mSlowDeliveryDetected赋值true,用来表示队列中有一个消息投递的慢了,
                // 直到遇见下一个投递时间差没有超出10毫秒,再将其赋值false,也就是上面的if条件语句。
                me.mSlowDeliveryDetected = true;
            }
        }
    }
    if (logSlowDispatch) {
        // 如果消息派发处理时间超出阈值,打印警告日志。
        showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
    }

    // 如果我们设置了Printf,则打印处理完毕的日志
    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();

    return true;
}

上面有两个方法我们没有展开将,一个是 MessageQueue 的 next()方法,一个是 Handler 的 dispatchMessage 方法,下面开始讲解。

4.3.2 MessageQueue 的 next()

需要注意的是,此时的 MessageQueue 里面的 Message 已经按照其延时时间 when 从小到大排序完成,when 越小越靠前靠近队头。

java 复制代码
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.
    // 调用quit方法后,mPtr会赋值0,这里就会返回null,结束队列的循环运转。
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    // 这里又出现一个新名词,IdleHandler
    // 后面会详细解释
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    // 下一个消息执行还需要等待的时间
    int nextPollTimeoutMillis = 0;
    // 使用一个for循环寻找下一个要执行的Message,
    // 找到后将Message返回。
    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;
            // 如果遇到同步屏障,则其后面的同步消息将都会略过不执行,
            // 只执行异步消息,除非调用removeSyncBarrier移除同步屏障,
            // 这种情况只有当同步屏障成为队头时,也就是如果同步屏障前面有
            // 同步消息,则会先执行前面的同步消息。
            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
            if (msg != null) {
                // 如果当前时间now还小于msg的执行时间,则计算下还需要等待的时间
                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.
                    // 否则msg已经到了可以执行的时间,需要返回
                    // 同时mBlocked置为false
                    mBlocked = false;
                    if (prevMsg != null) {
                        // 走到这里说明找到的是异步msg
                        // 从队列中移除找到的msg
                        prevMsg.next = msg.next;
                    } else {
                        // 走到这里说明找到的msg是队头,
                        // 将msg的下一个元素置为新的队头。
                        mMessages = msg.next;
                    }

                    // 这里将msg标志为正在使用,然后返回。
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 如果msg为null,说明队列中没有要执行的消息了,
                // 或者此时表头为同步屏障,但是队列中没有异步消息
                // No more messages.
                // 阻塞,直到下个Message入队
                nextPollTimeoutMillis = -1;
            }

            // 如果在运行过程中,有地方调用了Looper的quit方法,则退出loop循环操作。
            // 此后所有的msg都将不会执行,除非再次调用Looper.loop()启动运行
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                // dispose函数用于释放
                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.

            // 从这里开始都next()函数结束,剩下的代码都是IdleHandler相关的了。
            // 请注意,这里的代码还在上面的for循环里,for循环开始前pendingIdleHandlerCount赋值了-1。
            // 所以,第一次for循环 或者 队头msg为空(mMessages == null)
            // 或者队头的msg还未到执行时间(now < mMessages.when),
            // 就以mIdleHandlers的size重新赋值pendingIdleHandlerCount。
            // 其中mIdleHandlers是IdleHandler的一个列表,初始为空。
            // IdleHandler是一个接口类型,其有一个方法boolean queueIdle().
            // 列表mIdleHandlers需要通过MessageQueue的addIdleHandler方法添加元素,
            // 通过MessageQueue的removeIdleHandler方法删除元素。
            // IdleHandler存在的意义是当MessageQueue空闲时可以执行一些额外的任务。
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            // 如果pendingIdleHandlerCount<=0,说明没有需要执行的IdleHandler
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            // mPendingIdleHandler是IdleHandler类型的数组,其最小长度为4。
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            // 将mPendingIdleHandlers中的元素赋值到mPendingIdleHandlers数组中
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        // 遍历mPendingIdleHandlers数组,执行其queueIdle()函数。
        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);
            }

            // 如果queueIdle()函数返回false,则将其IdleHandler从列表中移除。
            // 这样下次执行next()时,将不会再执行这个IdleHandler的queueIdle()方法
            // 否则若queueIdle()函数返回true,则下次next()时就还会执行其
            // queueIdle()函数。
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // 到这里,如果有IdleHandler的话,则已经执行完一轮。
        // 此时需要重置pendingIdleHandlerCount = 0
        // 这样下次for循环时,就不会再执行其queueIdle函数了。
        // 下次执行要在下一次next()函数执行时继续按照上面的规则判定是否执行。
        // 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.
        // 走到这里说明队列空闲时执行了额外的IdleHandler任务,此时可能已经到了下一个Message需要
        // 执行的时间,所以将休眠唤醒时间赋值为0,也就是下次for循环时直接唤醒线程。
        nextPollTimeoutMillis = 0;
    }
}

4.3.3 Handler 的 dispatchMessage

dispatchMessage 是 Message 出队后的最后一步,进行消息处理。

java 复制代码
public void dispatchMessage(@NonNull Message msg) {
    // 如果是通过post Runnable发送的消息,则会回调Runnable的run方法
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        // 如果构造Handler时传入了Callback参数,则回调
        // Callback的handleMessage方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //如果以上callback都没有,最后才是执行Handler的
        // handleMessage方法
        handleMessage(msg);
    }
}

5、Looper quit

最后分析一下 quit 方法的实现,前面讲过,quit 有两个方法,分别是 quit() 和 quitSafely(),其最终会调用到 MessageQueue 的 quit 方法中

java 复制代码
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);
    }
}

5.1 removeAllMessagesLocked

这个比较简单,遍历队列,对每个 Message 进行回收,最后将队头 mMessages 赋值为 null。

java 复制代码
private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

5.2 removeAllFutureMessagesLocked

这个函数相比 removeAllMessagesLocked 多了一个 Future 字段,字面意思是未来,我们猜测这个函数移除的是还未到执行时间的 Message,如果 Message 已经到了执行时间,在投递阶段,还未派发完成,则需要执行完成。

下面看下实现跟我们猜测是否一样。

java 复制代码
private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
        // 队头还未到执行时间,那所有Message都未到,
        // 队列里的Message都属于Future Message,
        // 直接调用removeAllMessagesLocked即可。
        if (p.when > now) {
            removeAllMessagesLocked();
        } else {
            //
            Message n;
            for (;;) {
                n = p.next;
                // 到这里说明,队列中的Message都在投递状态,
                // 都不属于Future Message,所以直接return。
                if (n == null) {
                    return;
                }
                // 找到了还未到执行时间的msg,
                // 那么它后面的肯定也都是Future msg
                // 退出遍历循环即可。
                if (n.when > now) {
                    break;
                }
                //
                p = n;
            }
            // 此时的n属于找到的第一个Future Message
            // 此时p 是 n的前一个Message
            // 下面要把p后面,从n开始的msg全部回收
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}

6、 Message 池管理

最后有必要再看下 Message 的池管理。

有一个池子,里面原来是空的,当我们创建一个 Message 使用完进行回收后,并没有把这个 Message 对象销毁,而是放到了这个池子里,这个池子最大能容纳 50 个 Message 对象。当需要创建 Message 对象使用时,可以查看下池子是否为空,如果不是,就取一个拿出来使用。这样可以避免创建更多的 Message 对象,一定程度上达到节省内存和申请对象带来的开销。

Message 池也使用了单项链表的数据结构。

java 复制代码
// frameworks/base/core/java/android/os/Message.java

// 下面几个变量是管理Message池子的一些工具变量
/** @hide */
public static final Object sPoolSync = new Object();
// sPool是池子的表头元素
private static Message sPool;
// 池子中的Message元素个数
private static int sPoolSize = 0;
// 最大能存放50个Message元素
private static final int MAX_POOL_SIZE = 50;

// Android 5.0之后这个值都为true
private static boolean gCheckRecycle = true;


/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    // 如果池子不是空的,则将其表头Message拿出来返回
    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 的回收函数

java 复制代码
public void recycle() {
    // 如果回收正在使用的msg,则抛出异常
    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 = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    // 如果当前线程池的Message数量小于MAX_POOL_SIZE,
    // 则将当前回收的Message放入表头,sPoolSize递增
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}
相关推荐
数据猎手小k1 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小102 小时前
JavaWeb项目-----博客系统
android
风和先行2 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.3 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰4 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶4 小时前
Android——网络请求
android
干一行,爱一行4 小时前
android camera data -> surface 显示
android
断墨先生4 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员6 小时前
PHP常量
android·ide·android studio
萌面小侠Plus7 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机