android 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.prepare ThreadLocal Looper MessageQueue android_os_MessageQueue NativeMessageQueue Looper.cpp pthread.h get null new new android_os_MessageQueue_nativeInit new getForThread pthread_once(& gTLSOnce, initTLSKey) 确保从任何线程对 getForThread 的首次调用将执行 initTLSKey,initTLSKey 方法会调用 pthread_key_create 来创建一个线程特有数据的键,并将其存储于全局变量 gTLSKey 中, 同时记录释放与键对应的线程特有数据缓冲区的析构方法 threadDestructor 的地址 result pthread_getspecific(gTLSKey) 获取该线程中对应于 gTLSKey 的唯一缓冲区地址,若返回 null,表示该线程首次调用 getForThread。此时应该返回 null 以便继续往下走 (Looper*) mLooper new (false) mLooper setForThread(mLooper) pthread_setspecific(gTLSKey, looper.get()) 将刚才新分配的 Looper 对象地址保存到该线程的特有数据缓冲区中 nativeMessageQueue mPtr mQueue anonymous Looper object set(anonymous Looper object) Looper.prepare ThreadLocal Looper MessageQueue android_os_MessageQueue NativeMessageQueue Looper.cpp pthread.h

Looper::Looper native 构建流程

Looper.cpp eventfd.h epoll.h eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC) reset mWakeEventFd rebuildEpollLocked epoll_create1(EPOLL_CLOEXEC) reset mEpollFd epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), ) result void Looper.cpp eventfd.h epoll.h

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

ThreadLocal.get()

ThreadLocal Thread ThreadLocalMap ThreadLocalMap.Entry currentThread t getMap(t) return t.threadLocals to map 如果 t.threadLocals 为 null,则会走初始化逻辑 setInitialValue initialValue null to value currentThread t getMap(t) t.threadLocals to map, so map is null at this time createMap(t, value) new new 一个 Entry 数组,初始容量为 16 table t.threadLocals void null ThreadLocal Thread ThreadLocalMap ThreadLocalMap.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 方法,看下时序图:

MessageQueue android_os_MessageQueue.cpp NativeMessageQueue Looper.cpp epoll.h unistd.h nativePollOnce pollOnce pollOnce pollInner(timeoutMillis) epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis) 传入的 timeoutMillis,决定了不堵塞、一直堵塞、最长堵塞多久 eventCount 若 eventCount 大于 0,代表有 eventCount 个文件描述符处于就绪状态,这些就绪的文件描述符置于数组 eventItems 中。若 eventItems 中有 mWakeEventFd,则执行唤醒 awoken read(mWakeEventFd.get(), , ) void loop [死循环] void void MessageQueue android_os_MessageQueue.cpp NativeMessageQueue Looper.cpp epoll.h unistd.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 方法,继续:

MessageQueue android_os_MessageQueue.cpp NativeMessageQueue Looper.cpp unistd.h nativeWake wake wake write(mWakeEventFd.get(), &inc, sizeof(uint64_t)) uint64_t inc = 1,也即向 mWakeEventFd 中写入了个占8字节的整型值,该值为 1 void void void MessageQueue android_os_MessageQueue.cpp NativeMessageQueue Looper.cpp unistd.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

HandlerThread Process Looper Object myTid() myTid prepare() myLooper() mLooper notifyAll() setThreadPriority(mPriority) onLooperPrepared() loop() HandlerThread Process Looper Object

Handler_BlockingRunnable::postAndWait

Handler_BlockingRunnable Handler MessageQueue post(this) getPostMessage sendMessageDelayed sendMessageAtTime enqueueMessage enqueueMessage Handler_BlockingRunnable Handler MessageQueue
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值