android Handler 机制源码解读

文章深入探讨了Android消息处理机制的核心组件Handler、MessageQueue、Looper及ThreadLocal的工作原理,强调了Linux/Unix系统编程知识对理解Android基础流程的重要性。通过介绍epollIO模型和线性探查法等底层实现细节,阐述了消息驱动机制如何高效运作,并涉及PthreadsAPI在Handler机制中的应用。

最近正好赋闲在家,索性趁这段时间好好整理下 android 的一些基础流程。我做 android 也十几年了,平时一直感觉忙忙碌碌,平时虽有整理,但比较零碎,而且也不持续,东一锤西一锤的,就忘了。我认为,要想彻底了解清楚android 的一些基本原理,还是得看点系统编程方面的书的,之所以这么说,是因为我之前一直以为遇到不会的知识点上网查就好了,但查来查去,感觉不系统,时间一长还是觉得不清不楚。推荐阅读“Linux/Unix 系统编程手册”这本书,该书可以当工具书,也可以先通读一遍,读不懂没关系,读完后,可以结合 android 源码再有重点读,反复读就明白了。

本文档是基于 Android 11 分析的。

Handler 涉及的类有 Handler、Message、MessageQueue、Looper,ThreadLocal。一个线程只能创建一个 Looper,因此 Looper 是和线程绑定的,主线程创建的 Looper 称为主 Looper,是不能退出的,否则进程也会退出。

Handler 底层实现涉及的基础知识

IO 模型 – epoll

为什么要用 epoll 机制?Android 应用是通过消息来驱动的,那处理消息和发消息不一定是处于同一个线程,既然这样,如果一个线程不停取消息,因为没消息处于阻塞的时候,别的线程将一个消息入队如何通知那个阻塞的线程有消息入队了呢?这就用到了 epoll。
epoll,是一种 IO 模型,Handler 中主要是用到了 epoll 机制来监听 MessageQueue 队列的变化。epoll 涉及的接口主要有:
epoll_create:创建一个新的 epoll 实例,其对应的兴趣列表初始化为空。
epoll_ctl:能够修改指定 epoll 实例中的兴趣列表,比如增删改。
epoll_wait:监视 epoll 实例中所携带兴趣列表中的文件描述符,监视这些文件描述符是否处于就绪状态。监视的时候是否等待及等待多久是根据传入的超时参数来决定的。如果参数 timeout 值为 -1,该调用将一直阻塞,直到兴趣列表中的文件描述符有事件产生,或者直到捕获到一个信号为止;如果 timeout 值为 0,执行一次非阻塞式的检查,看兴趣列表中的文件描述符上产生了哪个事件;如果 timeout 值大于0,调用将阻塞至多 timeout ms。

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,
		int maxevents, int timeout);

一般步骤是 epoll_create --> epoll_ctl --> epoll_wait,之后就可以从 epoll 实例的兴趣列表中写入内容了,然后 epoll 描述符监听到兴趣列表中的 fd 有写入,这时就可以从该 fd 中读内容了。

哈希函数 — 线性探查法

线性探查法是一种用来计算开放寻址法中的探查序列的技术。假设数组长为 m,给定一个哈希码 k,利用某种哈希函数对该 k 计算出的哈希值为 i,若槽 i 没被占用,则将该 k 放入该槽,否则探查槽 i+1,直至槽 m-1,然后又绕到槽 0,1,…,直至最后一个槽 i-1。
Thread 就是利用线性探查法往其 ThreadLocalMap 类型的成员变量 threadLocals 中添加元素的。

Pthreads API

Handler 机制里主要用了如下几个 Pthreads API,当然 Pthreads API 也不局限于如下几个:
pthread_once():用于实现一次性初始化。确保无论有多少线程对该函数调用了多少次,只会执行一次由其参数 init 指向的调用者定义函数。
pthread_key_create():为线程特有数据创建一个新键,并通过参数 key 所指向的缓冲区返回给调用者。参数 key_destructor 指向一个自定义函数,只要线程终止时与 key 的关联值不为 NULL,Pthreads API 会自动执行该解构函数,并将与 key 的关联值作为参数传入该解构函数。
pthread_getspecific():返回之前与本线程及给定参数 key 相关的值。
pthread_setspecific():将参数 value 的副本存储于一数据结构中,并将 value 与调用线程以及参数 key 相关联。

int pthread_once(pthread_once_t* once, void (*init)(void));
int pthread_key_create(pthread_key_t* key, void (*key_destructor)(void*));
void* pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void* value);

类关系及主要成员

要了解 Handler 原理,先认识下 Handler 涉及的类之间的关系:
(mermaid 流程图中,访问权限 + public,- private,# protected,~ package,带下划线的表示 static,而 final 修饰符貌似没法表示,所以得看下后面代码片段,毕竟有无 final 修饰对一个成员的影响还是蛮大的。)

类关系

java 层类关系:

*
1
T is specified to ThreadLocal in ThreadLocalMap
Reference<T>
~T referent
ReferenceQueue<?> queue
ThreadLocal_ThreadLocalMap_Entry
~Object value
ThreadLocal_ThreadLocalMap
-Entry[] table
Thread
ThreadLocal.ThreadLocalMap threadLocals
Handler
<final Looper mLooper
<final MessageQueue mQueue
Looper
~ThreadLocal sThreadLocal
-Looper sMainLooper
~MessageQueue mQueue
+prepare()
+prepareMainLooper()
-prepare(bool)
+getMainLooper()
MessageQueue
~Message mMessages
Message
+int what
~int flags
+long when
~Handler target
~Runnable callback
~Message next
-Message sPool
ThreadLocal<Looper>
WeakReference<T>

native 层类关系:

«android_os_MessageQueue.h»
MessageQueue
#sp<Looper> mLooper
Looper
-unique_fd mWakeEventFd
-unique_fd mEpollFd
LooperCallback
+virtual int handleEvent(int fd, int events, void* data)
RefBase
NativeMessageQueue
unique_fd

类主要成员

了解了其类关系后,继续加强看下 Handler 涉及的类有哪些成员变量及构造函数:

Handler

Handler 主要有如下成员变量:

    @UnsupportedAppUsage
    final Looper mLooper;
    final MessageQueue mQueue;
    @UnsupportedAppUsage
    final Callback mCallback;
    final boolean mAsynchronous;
    @UnsupportedAppUsage
    IMessenger mMessenger;

Looper 的成员变量 mLooper 和 mQueue 是 final 修饰的,只能初始化一次,一经初始化就不能改变了,这个得重点关注。
Handler 主要有如下构造函数:

    @Deprecated
    public Handler() {
        this(null, false);
    }

    @Deprecated
    public Handler(@Nullable Callback callback) {
        this(callback, false);
    }

    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }

    public Handler(@NonNull Looper looper, @Nullable Callback callback) {
        this(looper, callback, false);
    }

    @UnsupportedAppUsage
    public Handler(boolean async) {
        this(null, async);
    }

    public Handler(@Nullable 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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

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

从其构造函数来看可以分为参数带 Looper 类型和不带 Looper 类型的。如果采用不带 Looper 类型参数的构造函数构造 Handler 对象,则在构造 Handler 对象前,需要确保构造 Handler 对象的所在线程已经有相应的 Looper 对象了。一个应用进程主线程的 Looper 对象是在 ActivityThread 的 main 方法中通过调用 Looper.prepareMainLooper() 来创建的,也就是说主 looper 已经由 android 运行时环境为用户建好了,而如果用户需要在子线程中使用 looper 对象,那么需要用户在子线程中调用 Looper 的 prepare() 静态方法先创建个 Looper 对象,然后再创建 Handler 对象。

Looper

Looper 主要有如下成员变量:

    // sThreadLocal.get() will return null unless you've called prepare().
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    @UnsupportedAppUsage
    private static Looper sMainLooper;  // guarded by Looper.class
    private static Observer sObserver;

    @UnsupportedAppUsage
    final MessageQueue mQueue;
    final Thread mThread;
    private boolean mInLoop;

而 Looper 构造函数只有一个:

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

从其定义来看,该构造函数是 private 的,只能在类内部构建,那在 Looper.java 中搜下 new Looper,看看哪个方法会调用,如下:

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

喔?在静态私有的 prepare(boolean quitAllowed) 这个方法里会 new Looper,而且会将 new 出来的该对象 set 给 sThreadLocal,从这里可以看出 Looper 是和线程绑定的了,那其原理是啥?稍后再做介绍。既然带 boolean 参数的 prepare 方法是私有的,那肯定它也是被某个公开的 public 方法调用的,继续在该类里搜,找到如下:

    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      * 翻译:为当前线程初始化一个 looper,这样你可以创建引用这个 looper 的 handler,
      * 为使线程开始循环,需要在调用这个方法之后调用 loop() 方法,如果想结束它,可以调用
      * quit() 方法。
      */
    public static void prepare() {
        prepare(true);
    }

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. See also: {@link #prepare()}
     *
     * @deprecated The main looper for your application is created by the Android environment,
     *   so you should never need to call this function yourself.
     *   翻译:为当前线程创建一个 looper,而且把其标记成应用的主 looper。
     *   应用程序的主 looper 是由 android 运行环境创建的,用户永远不应该自己调用这个函数。
     */
    @Deprecated
    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 的无参 prepare 和无参 prepareMainLooper 方法里会看到这两方法分别调用了 prepare(true) 和 prepare(false)。从这两个方法的注释可以看出,用户如果想为一个线程创建一个 Looper 对象,可以调用 prepare(),而 prepareMainLooper() 方法用户是不能调用的,该方法仅供 android 运行时使用。

MessageQueue

MessageQueue 关键成员变量:

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

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

	@UnsupportedAppUsage
    Message mMessages;

MessageQueue 构造函数:

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        /*
         * 这个主要是在 native 层创建了一个 wake event fd及 epoll 实例,
         * 然后将 wake event fd 添加到该 epoll 实例的兴趣列表中
         * 之后该 message queue 有 message 入队是否唤醒,取 message 是否阻塞就是靠这个 epoll 实例实现的
         */
        mPtr = nativeInit();
    }

Message

    /**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     * 用户定义的消息码,接收者用此码来区分消息。每个 Handler 会定义自己的消息码。
     */
    public int what;

	/** If set message is asynchronous */
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    @UnsupportedAppUsage
    /*package*/ int flags;
    
    /**
     * The targeted delivery time of this message. The time-base is
     * {@link SystemClock#uptimeMillis}.
     * @hide Only for use within the tests.
     */
    @UnsupportedAppUsage
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public long when;

    @UnsupportedAppUsage
    /*package*/ Handler target;
    
    // sometimes we store linked lists of these things
    @UnsupportedAppUsage
    /*package*/ Message next;

	private static Message sPool;

Message 分异步和同步,通过成员变量 flags 是否携带 FLAG_ASYNCHRONOUS 位来判断;而同步消息又细分为同步障碍消息和普通同步消息,普通同步消息带 target,同步障碍消息不带 target,而且同步障碍消息用户不能发,只能由系统发。Message 在 Message 队列中是按照时间排序的,这个等下分析 MessageQueue 的 next 方法时会看到。使用 Message 的时候,不需要使用者去 new 一个对象,而是调用类 Message 的静态成员方法 obtain 来获取,获取的时候会从 Message 的 sPool 中取,若 sPool 为 null,则会 new 个 Message 对象;用完需要回收该 Message 对象,即把 Message 对象放入 sPool 中。 因为 android 是 Message 驱动的,所以这样做可以减少频繁的内存分配。

ThreadLocal

ThreadLocal 是一个模板类,还是先看下其关键成员变量:

    /**
     * ThreadLocals rely on per-thread linear-probe hash maps attached
     * to each thread (Thread.threadLocals and
     * inheritableThreadLocals).  The ThreadLocal objects act as keys,
     * searched via threadLocalHashCode.  This is a custom hash code
     * (useful only within ThreadLocalMaps) that eliminates collisions
     * in the common case where consecutively constructed ThreadLocals
     * are used by the same threads, while remaining well-behaved in
     * less common cases.
     * 翻译: ThreadLocals 依赖于每个线程的哈希表(Thread.threadLocals 和 inheritableThreadLocals),
     * 该哈希表采用线性探测算法(linear-probe)。ThreadLocal 作为键,按照 threadLocalHashCode 去查找。
     * threadLocalHashCode 是一个自定义哈希码,仅在 ThreadLocalMaps 内部使用,
     * 该哈希码是用来消除常见情况下在同一个线程连续创建 ThreadLocal 对象时产生的冲突,
     * 在一些不太常见的情况下也表现挺好。
     */
    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     * 下一个哈希码,进程中创建一个 ThreadLocal 对象,该变量更新一次。初始值是 0.
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     * 翻译:连续生成的哈希码之间的差,将隐式顺序 thread-local id 转换成
     * 接近最优散列乘法哈希值到大小为 2的 n 次方的数组中
     */
    private static final int HASH_INCREMENT = 0x61c88647;

threadLocalHashCode 是 private final 修饰的,也即只能在该类内部使用,其值不能改变;而 nextHashCode 和 HASH_INCREMENT 是 private static 的,也即同一个进程的各个 ThreadLocal 对象共享这两成员变量,而且 HASH_INCREMENT 值不能改变;
ThreadLocal 构造函数:

    /**
     * Creates a thread local variable.
     * @see #withInitial(java.util.function.Supplier)
     */
    public ThreadLocal() {
    }

ThreadLocal 只有一个构造函数,而且其函数体是空的。说明 ThreadLocal 中应该包含别的重要的成员方法,之前说 Looper 是和线程绑定的,而且 Looper 的私有静态成员方法 prepare(boolean quitAllowed) 中就用到了 ThreadLocal 的 get 和 set 方法,所以再接着看下这两方法的实现:

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

先看 ThreadLocal 的 get 方法,该方法中用到了 Thread 和 ThreadLocalMap,所以得继续看下 ThreadLocalMap 相关的成员变量及构造函数,而对于 Thread 类来说,其关键成员方法大部分是 native 的,这块先不细究,只需知道其作用及有哪些关键成员即可。

ThreadLocal.ThreadLocalMap

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0

        /**
         * Set the resize threshold to maintain at worst a 2/3 load factor.
         */
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }

        /**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        	//创建了一个数组,初始大小为 16
            table = new Entry[INITIAL_CAPACITY];
            //取传入的参数 firstKey 携带的 threadLocalHashCode 的低4位作为该 key 在数组中的索引
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            //以传入的 firstKey 和 firstValue 构建一个 Entry 对象,并将该对象保存到数组对应的索引中
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            //设置阈值,数组现有元素总数超过该阈值时,就会对数组扩容,按现有数组大小的2倍扩
            setThreshold(INITIAL_CAPACITY);
        }

Thread

Thread 类的关键成员变量及方法如下,对于该类不打算深挖,咱只要知道其作用是啥即可。

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. 
     * 翻译:属于这个线程的 ThreadLocal 值。这个 map 是由类 ThreadLocal 来维护的。
     */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    
    /**
     * Returns a reference to the currently executing thread object.
     *
     * @return  the currently executing thread.
     * 返回当前正在执行线程对象的一个引用
     */
    @FastNative
    public static native Thread currentThread();

流程解读

以下以 ActivityThread main 方法建立主 looper 为例来解读:

    public static void main(String[] args) {
        ......
		
		/*
         * prepareMainLooper 方法的主要作用是创建一个 Looper 对象,
         * 并将该 Looper 对象与该线程绑定;创建 Looper 对象的时候会同步创建
         * MessageQueue 对象,该 MessageQueue 对象主要是初始化 epoll 实例
         * 以便监听 MessageQueue 队列的变化
        */
        Looper.prepareMainLooper();
        ......
        /*
         * ActivityThread 类有一个成员变量 mH,mH 是 H 类型,而类 H 继承自类 Handler,
         * 因此构造 ActivityThread 对象实例的时候,其成员变量 mH 也会被构建。
         * 在构建 mH 的时候,会获取调用线程所持有的 Looper 对象及该 Looper 对象持有的
         * MessageQueue 对象,并将其保存到 mH 相应的成员变量中。
        */
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        /*
         * 从 main 函数进来的时候,ActivityThread 类的静态成员变量
         * sMainThreadHandler 为 null,因此该 if 判断块会执行
         */
        if (sMainThreadHandler == null) {
            // thread.getHandler() 返回的是 thread 持有的 mH 变量
            sMainThreadHandler = thread.getHandler();
        }

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

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // Looper 的静态方法 loop 函数体是一个死循环
        Looper.loop();

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

所以先看下 Looper 的静态成员方法 prepareMainLooper() --> prepare(false) 的一个调用流程:

Looper.prepare(false) jni之前

Looper.prepareThreadLocalLooperMessageQueueandroid_os_MessageQueueNativeMessageQueueLooper.cpppthread.hgetnullnewnewandroid_os_MessageQueue_nativeInitnewgetForThreadpthread_once(& gTLSOnce, initTLSKey)确保从任何线程对 getForThread 的首次调用将执行 initTLSKey,initTLSKey 方法会调用 pthread_key_create 来创建一个线程特有数据的键,并将其存储于全局变量 gTLSKey 中, 同时记录释放与键对应的线程特有数据缓冲区的析构方法 threadDestructor 的地址resultpthread_getspecific(gTLSKey)获取该线程中对应于 gTLSKey 的唯一缓冲区地址,若返回 null,表示该线程首次调用 getForThread。此时应该返回 null 以便继续往下走(Looper*)mLoopernew (false)mLoopersetForThread(mLooper)pthread_setspecific(gTLSKey, looper.get())将刚才新分配的 Looper 对象地址保存到该线程的特有数据缓冲区中nativeMessageQueuemPtrmQueueanonymous Looper objectset(anonymous Looper object)Looper.prepareThreadLocalLooperMessageQueueandroid_os_MessageQueueNativeMessageQueueLooper.cpppthread.h

Looper::Looper native 构建流程

Looper.cppeventfd.hepoll.heventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)reset mWakeEventFdrebuildEpollLockedepoll_create1(EPOLL_CLOEXEC)reset mEpollFdepoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), )resultvoidLooper.cppeventfd.hepoll.h

从以上时序图可以看出,所谓的 Looper.prepare 最终是初始化了一个 epoll 实例,并创建了一个 event fd,然后将该 fd 加到了该 epoll 实例的兴趣列表中。那为啥说一个线程只能有一个 Looper 对象呢?这是由 ThreadLocal 这个类来实现的,先看到该类的 get 方法的调用流程:

ThreadLocal.get()

ThreadLocalThreadThreadLocalMapThreadLocalMap.EntrycurrentThreadtgetMap(t)return t.threadLocals to map如果 t.threadLocals 为 null,则会走初始化逻辑setInitialValueinitialValuenull to valuecurrentThreadtgetMap(t)t.threadLocals to map, so map is null at this timecreateMap(t, value)newnew 一个 Entry 数组,初始容量为 16tablet.threadLocalsvoidnullThreadLocalThreadThreadLocalMapThreadLocalMap.Entry

好了,至此应该知道了一个线程是如何只能绑定一个 Looper 对象了。
继续看下 Message 入队出队流程。
ActivityThread main 方法里,最后会调用 Looper.loop() 陷入死循环,否则就抛出异常退出。所以,Looper.loop() 很重要,先大致看下该方法实现:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        //1.获取当前线程的 Looper 对象
        final Looper me = myLooper();
        ...
        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            //2. 从 MessageQueue 中取一条 Message
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...
            try {
                //3. 向目标 Handler 分发 Message
                msg.target.dispatchMessage(msg);
                ...
            } catch (Exception exception) {
                ...
                throw exception;
            } finally {
                ...
            }
            ...            
            //4. 回收该 Message
            msg.recycleUnchecked();
        }
    }

从该方法实现上看,在获取了该线程持有的 Looper 对象后,就陷入死循环,不停取Message,分发 Message,然后回收 Message,若无 Message,该方法就直接返回了。
先看下取 Message 时 queue.next() 的代码实现:

    @UnsupportedAppUsage
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

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

            /*
             * 第一次进入循环的时候,nextPollTimeoutMillis 为 0
             */
            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;
                /*
                 * 如果 Message 对象的 target 为 null,则将该 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());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.代表当前队列不是处于阻塞状态
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

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

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    // 当前队列没消息,也没 idle handler 要处理,说明需要阻塞等待新消息到来
                    mBlocked = true;
                    continue;
                }

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

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

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

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

next() 方法中最重要的一句 nativePollOnce() 调用,这个是 native 方法,看下时序图:

MessageQueueandroid_os_MessageQueue.cppNativeMessageQueueLooper.cppepoll.hunistd.hnativePollOncepollOncepollOncepollInner(timeoutMillis)epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis)传入的 timeoutMillis,决定了不堵塞、一直堵塞、最长堵塞多久eventCount若 eventCount 大于 0,代表有 eventCount 个文件描述符处于就绪状态,这些就绪的文件描述符置于数组 eventItems 中。若 eventItems 中有 mWakeEventFd,则执行唤醒awokenread(mWakeEventFd.get(), , )voidloop[死循环]voidvoidMessageQueueandroid_os_MessageQueue.cppNativeMessageQueueLooper.cppepoll.hunistd.h

说白了,nativePollOnce() 方法的作用就是采用 epoll 机制监听 mWakeEventFd 中是否有读事件发生,以便唤醒消息队列取消息。
Loop.loop() 取到消息后,接下来就是往目标 handler 中分发该消息,所以看下类 Handler 的 dispatchMessage() 方法的实现:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }

从 dispatchMessage() 方法实现来看,Message 对象中携带的 callback 优先,Handler 对象的 mCallback 次之,handleMessage() 用来打底。 mCallback 是在构建 Handler 对象的时候,用户可以传一个非空的 Callback 对象(Callback 是一个接口,用户需自定义实现它。);若没传 Callback 对象,则一般用户需要自定义一个 Handler 的子类,然后在该子类中重写 handleMessage()。
取消息到此已经解读完,接下来该看消息是如何入队的。Handler 中与消息入队的方法如下:

Handler
+boolean sendMessage(@NonNull Message msg)
+boolean sendEmptyMessage(int what)
+boolean sendEmptyMessageDelayed(int what, long delayMillis)
+boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
+boolean sendMessageDelayed(@NonNull Message msg, long delayMillis)
+boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
+boolean sendMessageAtFrontOfQueue(@NonNull Message msg)
-boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis)

send 开头的这几个公开的方法,最终都是调用私有的 enqueueMessage() 方法,所以着重看下该方法的实现:

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

接着看 MessageQueue 的 enqueueMessage() 方法实现:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                /*
                 * 如果当前队列拿到了下一个消息(即当前正在处理消息),代表不需要唤醒;
                 * 若当前无消息处理,说明队列处于堵塞状态,需要唤醒
                 */
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                // 如果队列因同步障碍消息阻塞且当前入队消息是异步消息,则暂时标记为需要唤醒,
                // 最后是否需要唤醒还要看该异步消息携带的时间戳,若晚于队头消息的时间戳,也不需要唤醒,
                // 因为还没到处理该异步消息的时间点
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

在需要唤醒的时候,会调用 nativeWake() 方法,该方法是 native 方法,继续:

MessageQueueandroid_os_MessageQueue.cppNativeMessageQueueLooper.cppunistd.hnativeWakewakewakewrite(mWakeEventFd.get(), &inc, sizeof(uint64_t))uint64_t inc = 1,也即向 mWakeEventFd 中写入了个占8字节的整型值,该值为 1voidvoidvoidMessageQueueandroid_os_MessageQueue.cppNativeMessageQueueLooper.cppunistd.h

将一个 Message 入队,实际是通过向 mWakeEventFd 中写入个值来唤醒 Looper.loop() 线程的。
至此,Handler 入队消息,处理消息相关代码解读完了。
下面再简要介绍下 HandlerThread :

HandlerThread

HandlerThread 其实主要在该线程中创建了一个可以退出的 Looper,然后别的线程可以利用绑定了这个线程 Looper 的 Handler 向这个线程发送消息。一般会利用 Handler 的 runWithScissors 这个方法发送一个 Runnable 对象消息。

类结构

HandlerThread
~int mPriority
~int mTid
~Looper mLooper
-Handler mHandler
+HandlerThread(String name)
+HandlerThread(String name, int priority)
#void onLooperPrepared()
+void run()
+Looper getLooper()
+Handler getThreadHandler()
+boolean quit()
+boolean quitSafely()
+int getThreadId()
Handler_BlockingRunnable
-final Runnable mTask
-boolean mDone
+BlockingRunnable(Runnable task)
+void run()
+boolean postAndWait(Handler handler, long timeout)
Thread
Looper
Handler
Runnable

关键方法实现

HandlerThread::run

HandlerThreadProcessLooperObjectmyTid()myTidprepare()myLooper()mLoopernotifyAll()setThreadPriority(mPriority)onLooperPrepared()loop()HandlerThreadProcessLooperObject

Handler_BlockingRunnable::postAndWait

Handler_BlockingRunnableHandlerMessageQueuepost(this)getPostMessagesendMessageDelayedsendMessageAtTimeenqueueMessageenqueueMessageHandler_BlockingRunnableHandlerMessageQueue
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值