版本基于:Android R
0. 前言
Android handler 处理消息机制是Android 系统、应用开发中一个十分重要的利器,可以简单、轻松的将事情异步分解处理。
Handler 与Looper、MessageQueue、Message类紧密结合,实现了完整的消息机制。
1. 框架
注意:
Handler 发消息时,MessageQueue 是固定的,而MessageQueue 是在固定线程的固定Looper 中,所以最终使用哪个线程的Handler 发消息,最终处理函数handleMessage() 也应该是在哪个线程中运行。
2. Message
装载这消息的所有信息
- what: 消息的类别
- when:消息触发时间
- arg1,arg2,obj,data:消息的具体数据
- target:消息的去向,Handler 对象,消息可能通过别的Handler 对象进行send,但是取出msg dispatch的时候一定是通过这个target 去handleMessage
- callback:消息的Runnable,类似handleMessage
还需要注意是Message 有个全局的 sPool,最大限度为MAX_POOL_SIZE(默认 50) 个,如果小于MAX_POOL_SIZE,回收后的Message 会存放在sPool 中,所以,如果临时需要Messag 最好通过obtain() 的方式获取,节省内存同时也提高了性能。
3. Handler
3.1 Handler 构造
Handler 类的构造函数有7 个,但是最终可以提炼出两个,以参数有无looper 区分。
frameworks/base/core/java/android/os/Handler.java
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;
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
核心的几个变量:
- mLooper 如果不是由外面传入,那么Handler 的整个处理过程都在本线程中进行;如果由外面传入,则可以实现跨线程消息传输;
- mQueue 通过mLooper 获得,在sendMessage() 时需要将message 传入MessageQueue,MessageQueue 是由Looper 创建的;
- mCallback 为消息处理的核心函数,即handleMessage()
在简单的解析Handler 的构造后,了解一些基本的概念:
- Message: 消息,可能有硬件产生,也可能由软件产生,传输的基本单元;
- MessageQueue:消息队列,存放Message,是整个消息机制的核心类,需要串联整个消息流程;
- Looper:提供循环,一直处理消息的场所;
- Handler:消息机制的辅助类,消息的发起、处理都是通过Handler;
3.2 sendMessageAtTime
frameworks/base/core/java/android/os/Handler.java
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
...
return enqueueMessage(queue, msg, uptimeMillis);
}
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);
}
通过Handler 发送消息,无论是post(),还是sendMessage(),最后调用的是sendMessageAtTime() 函数,而在该函数中最终通过 MessageQueue.enqueueMessage() 将message 添加到 queue 中。详细看下面的第 5.5 节。
4. Looper
4.1 Looper 构造
frameworks/base/core/java/android/os/Looper.java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Java 端的Looper 构造用来创建Java 端的MessageQueue 实例,Looper 根本上只是又来提供一个循环,Looper 永远是活在创建它的线程中。
但是,JAVA Looper 的构造函数是private,只能通过prepare 接口获取。
4.2 prepare()
frameworks/base/core/java/android/os/Looper.java
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
在代码中,变量 sThreadLocal 的类型是ThreadLocal,该类的作用是提供“线程局部存储”,那什么是线程局部存储(TLS) 呢?这个问题可以从变量的作用域的角度来理解。
变量常见作用域分为以下几种:
- 函数内部的变量:其作用域是该函数,在函数调用的时候,该变量会重新初始化。
- 类内部的变量:其作用域是该类对象,即只要对象没有销毁,那么这个变量在对象内部的值是不变的。
- 类内部的局部变量:其作用域是整个进程,则只要在该进程中个,这个变量的值是一直保持,无论该类构造过多少对象,这个值是一样的。
对于类内部的变量而言,无论从进程中的哪个线程引用该变量,其值总是一样的。因为在编译器内部为这个局部变量独立分配了一个空间。但是,有时候我们想从一个线程中引用这个变量的值是一样的,不同的线程引用这个变量的时候值是不一样的,即我们需要一种作用域为线程变量定义,这就是”线程局部存储“。
libcoere/ojluni/src/main/java/java/lang/ThreadLocal.java
public class ThreadLocal<T> {
protected T initialValue() {
return null;
}
public ThreadLocal() {
}
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();
}
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;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
...
}
在get 的时候最开始返回的value 为null,通过set 后将value 存入ThreadLocalMap 中。
上面是ThreadLocal.java 中的部分code,可以看出ThreadLocal是个模板,这样就方便各种不同的对象类型。例如,Looper中的 sThreadLocal 是这样定义的:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
还有个细节需要注意,不能反复的在同一个线程中调用prepare(),第二次调用的时候sThreadLocal 中已经有值了,会抛出exception。
另外,需要注意:
Activity 的MainUI 线程默认是有消息队列的,所以在Activity 中新建Handler 时,不需要先调用Looper.prepare()。主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程,不会自动创建Looper,要需要的时候,通过调用prepare函数来实现。
4.3 loop()
frameworks/base/core/java/android/os/Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
...
try {
msg.target.dispatchMessage(msg);
...
}
...
msg.recycleUnchecked();
}
}
- loop() 函数必须是在prepare函数之后调用,不然会有提示:
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- for 循环是无限循环,会一直取消息,如果没有消息,会暂时的挂起来
- msg.target.dispatchMessage(msg); msg.target 就是Handler
frameworks/base/core/java/android/os/Handler.java
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {//传入到message中
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { //构造时传入
return;
}
}
handleMessage(msg); //继承Handler.Callback 后重写了handleMessage
}
}
这部分逻辑,详细可以查看《Android 中Handler 详解》一文中的第 2 节。
Java 的Looper 比native 的简单些,Looper.java 中提供一个循环,通过 msg.next() 反复的取出MessagQueue 中的msg,然后通过handleMessage() 进行处理。而 msg.next() 是一个阻塞的接口,等待MessageQueue 中存放一个新的message 的时候,会将这个阻塞唤醒,后面解析MessageQueue 时详细说明阻塞、监听机制。
5. MessageQueue
5.1 构造
frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
- mQuitAllowed 标记是否允许quit;
- 调用nativeInit 进行native 初始化;
下面会看到 MessageQueue 添加的message 都是存放在Java 端的,之所以引入native ,主要就是为了 epoll 机制使用。
5.2 nativeInit()
frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
在nativeInit() 中,new了一个 Native 层的 MessageQueue 对象,并将其地址保存在了Java层MessageQueue 的成员 mPtr 中,Android中有好多这样的实现,一个类在 Java 层与 Native 层都有实现,通过JNI的 GetFieldID() 与 SetIntField() 把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如 Parcel 类。
5.2.1 NativeMessageQueue的构造函数实现
frameworks/base/core/jni/android_os_MessageQueue.cpp
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
在 NativeMessageQueue 的构造函数中,主要是确定 native 端的 Looper 对象,而该对象通过两种方式获取:
- 线程局部变量,该Looper 已经创建好,并存在线程局部变量中;
- new Looper()
5.2.2 native Looper 的构造
system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks)
{
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
rebuildEpollLocked();
}
- 创建一个wakeEventFd
- 调用 rebuildEpollLocked(),初始化epoll
-->
继续来看rebuildEpollLocked()
void Looper::rebuildEpollLocked() {
...
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC)); //新建一个epoll fd
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd.get();
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem); //监听mWakeEventFd
...
}
通过 epoll_create1() 创建一个 epoll fd,并通过 epoll_ctl() 将 mWakeEventFd 加入到监听。
整个消息机制主要是在有新消息添加时唤醒阻塞,所以其他的暂时不解释,主要关注mWakeEventFd
5.3 nativePollOnce()
在上面第 4.3 节中,loop() 是一个死循环,通过 MessageQueue.next() 函数,获取一下个 message 进行处理,而在 next() 函数中通过 nativePollOnce() 进行 native 端的 wait,从而达到 next() 的阻塞:
frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
...
mLooper->pollOnce(timeoutMillis);
...
}
Looper->pollOnce() 的详细过程,这里不在过多的解释,后面有时间补充一个 Looper.cpp 的详细解析说明。
这里看wake 相关的部分:
system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) { //如果timeout小于0时,无限延时阻塞
timeoutMillis = messageTimeoutMillis;
}
}
...
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis); //等待唤醒或超时
// No longer idling.
mPolling = false;
...
// Check for poll timeout.
if (eventCount == 0) { //因为超时而唤醒epoll
result = POLL_TIMEOUT;
goto Done;
}
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd.get()) { //wake fd 被写数据了,通过wake()进行唤醒
if (epollEvents & EPOLLIN) {
awoken(); //只是把wake fd 中的数据读出来,之后不做过多的处理返回
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
...
}
...
return result;
}
见代码中注释。
5.4 next()
前期4.3 节中提到在调用MessageQueue.next() 的时候会进入阻塞,这里来看下如何阻塞?
frameworks/base/core/java/android/os/MessageQueue.java
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis); //进入阻塞,但是如果nextPollTimeoutMillis为0时,表示立即唤醒
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) { //注意barrier message
// 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); //如果唤醒的时候不够取出msg,循环回去继续阻塞
} 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; //取出msg,回到4.3 节通过Handler 进行dispatch
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
...
}
}
5.5 enqueueMessage()
从 3.2 节得知通过Handler 发送消息,最终调用的是 MessageQueue.enqueueMessage():
frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { //必须指定Handler,后期的处理依赖它
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) { //这个message 不能已经被send过了
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) { //MessageQueue 不能处于quitting 状态
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(); //send过来了,就标记上已使用
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 {
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) { //如果没有唤醒,则立马唤醒epoll
nativeWake(mPtr);
}
}
return true;
}
详细看代码中注释,核心部分就是nativeWake
5.6 nativeWake()
frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
Looper->wake() 就是向wake 的fd 中写数据用以唤醒epoll
-->
system/core/libutils/Looper.cpp
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
...
}
向wake fd 写了1,用以唤醒epoll,这样就可以回到 4.3 节进行messag的dispatch。
至此,Android 重要的消息机制已经剖析完成,后续native Looper 或单个模块的注意点再以另外的新文分析。
总结下:
- 通过Handler 向该线程的Looper 中MessageQueue 中添加message;
- MessageQueue 中如果有新的消息,会将原来阻塞(epoll wait)的流程唤醒,继续执行Looper 流程,将取出的Message 发送到其中的target所在的线程中进行处理;