最近正好赋闲在家,索性趁这段时间好好整理下 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 层类关系:
native 层类关系:
类主要成员
了解了其类关系后,继续加强看下 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::Looper native 构建流程
从以上时序图可以看出,所谓的 Looper.prepare 最终是初始化了一个 epoll 实例,并创建了一个 event fd,然后将该 fd 加到了该 epoll 实例的兴趣列表中。那为啥说一个线程只能有一个 Looper 对象呢?这是由 ThreadLocal 这个类来实现的,先看到该类的 get 方法的调用流程:
ThreadLocal.get()
好了,至此应该知道了一个线程是如何只能绑定一个 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 方法,看下时序图:
说白了,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 中与消息入队的方法如下:
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 方法,继续:
将一个 Message 入队,实际是通过向 mWakeEventFd 中写入个值来唤醒 Looper.loop() 线程的。
至此,Handler 入队消息,处理消息相关代码解读完了。
下面再简要介绍下 HandlerThread :
HandlerThread
HandlerThread 其实主要在该线程中创建了一个可以退出的 Looper,然后别的线程可以利用绑定了这个线程 Looper 的 Handler 向这个线程发送消息。一般会利用 Handler 的 runWithScissors 这个方法发送一个 Runnable 对象消息。