Android 为什么要用消息处理机制
如果有多个线程更新 UI,并且没有加锁处理,会导致界面更新的错乱,而如果每个更新操作都进行加锁处理,那么必然会造成性能的下降。所以在 Android 开发中,为了使 UI 操作是线程安全的,规定只许主线程即 UI 线程可以更新 UI 组件。但实际开发中,常常会遇到多个线程并发操作 UI 组件的需求,于是 Android 提供了一套消息传递与处理机制来解决这个问题。也就是在非主线程需要更新 UI 时,通过向主线程发送消息通知,让主线程更新 UI。
当然消息处理机制不仅仅可以用来解决 UI 线程安全问题,它同时也是一种线程之间沟通的方式。
Looper
Looper 即消息循环器,是消息处理机制的核心,它可以将一个普通线程转换为一个 Looper 线程。所谓的 Looper 线程就是一个不断循环的线程,线程不断循环的从 MessageQueue 中获取 Message,交给相应的 Handler 处理任务。在 Looper 类的注释介绍中,我们可以得知通过两个静态方法 Looper.prepare()
和 Looper.loop()
就可以将线程升级成 Looper 线程:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// 注意:一定要在两个方法之间创建绑定当前Looper的Handler对象,
// 否则一旦线程开始进入死循环就没法再创建Handler处理Message了
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 处理收到的消息
}
};
// 开始循环处理消息队列
Looper.loop();
}
}
Looper.prepare()
该方法在线程中将 Looper 对象定义为 ThreadLocal 对象,使得 Looper 对象成为该线程的私有对象,一个线程最多仅有一个 Looper。并在这个 Looper 对象中维护一个消息队列 MessageQueue 和持有当前线程的引用,因此 MessageQueue 也是线程私有。
public final class Looper {
// 每个线程仅包含一个的Looper对象,定义为一个线程本地存储(TLS)对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 每个Looper维护一个唯一的消息队列
final MessageQueue mQueue;
// 持有当前线程引用
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void prepare() {
prepare(true);
}
// 该方法会在调用线程的TLS中创建Looper对象,为线程私有
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper对象将抛出异常
throw new RuntimeException(
"Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
}
Looper.loop()
该方法启动线程的循环模式,从 Looper 的 MessageQueue 中不断的提取 Message,再交由 Handler 处理任务,最后回收 Message 供以后复用。
public static void loop() {
// 得到当前线程的Looper对象
final Looper me = myLooper();
if (me == null) {
// 线程没有调用Looper.prepare(),所以没有Looper对象
throw new RuntimeException(
"No Looper; Looper.prepare() wasn't called on this thread.");
}
// 得到当前消息队列
final MessageQueue queue = me.mQueue;
...
// 开始循环
for (;;) {
// 从消息队列中获取下一个Message,该方法可以被阻塞
Message msg = queue.next();
...
// 将Message推送到Message中的target处理
// 此处的target就是发送该Message的Handler对象
msg.target.dispatchMessage(msg);
...
// 回收Message,这样就可以通过Message.obtain()复用
msg.recycleUnchecked();
}
}
Handler
Handler 可以称之为消息处理者。Looper 线程不断的从消息队列中获取消息,而向消息队列中推送消息的正是 Handler 这个类。
Handler 在工作线程通过 sendMessage()
向 MessageQueue 中推送 Message,而主线程 Looper 循环得到 Message 后,即可得到发出该 Message 的 Handler 对象(Handler 发送消息时将自身引用赋值给 message.target
),再通过 Handler 对象的 dispatchMessage()
和 handleMessage()
方法处理相应的任务。这样我们就可以通过 Handler 同时完成了异步线程的消息发送与消息处理两个功能。
Handler 默认构造方法会关联当前线程的 Looper 对象,一个线程只能有一个 Looper 对象,但可以有多个 Handler 关联这个 Looper 对象。Handler 也提供一些构造方法关联自定义的 Looper 对象。
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
public Handler() {
this(null, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/**
* @hide
*/
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
// 如果打开了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());
}
}
// 关联到当前线程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
// 当前线程未调用Looper.prepare(),不是Looper线程
throw new RuntimeException(
"Can't create handler inside thread "
+ Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 持有当前Looper中消息队列的引用
mQueue = mLooper.mQueue;
mCallback = callback;
// 设置是否是异步消息
mAsynchronous = async;
}
/**
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
// 指定了Looper对象,Handler处理消息就会在该Looper对应的线程中执行
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
sendMessage()
Handler 的功能之一就是向消息队列发送消息,其有多个方法可以实现这个功能,如 post()
、postAtTime()
、postDelayed()
、sendMessage()
、sendMessageAtTime()
、sendMessageDelayed()
等等。post 一类的方法发送的是 Runnable 对象,send 一类的方法发送的是 Message 对象,但实际上 Runnable 对象最后都会封装成 Message 对象。
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
// 将Runnable封装成Message对象
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
// 将Runnable设为Message的callback变量
m.callback = r;
return m;
}
再看 sendMessage 类方法的源码:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 核心代码,sendMessage一路调用到此,让Message持有当前Handler的引用
// 当消息被Looper线程轮询到时,可以通过target回调Handler的handleMessage方法
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 将Message加入MessageQueue中,并根据消息延迟排序
return queue.enqueueMessage(msg, uptimeMillis);
}
dispatchMessage()
、handleMessage()
Handler 的另外一个主要功能就是处理消息。处理消息的核心代码即是 Looper.loop()
中的msg.target.dispatchMessage(msg)
以及 Handler 子类重写实现的 handleMessage()
回调方法。源码如下:
public interface Callback {
public boolean handleMessage(Message msg);
}
// 处理消息,该方法由Looper的loop方法调用
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 处理Runnable类的消息
handleCallback(msg);
} else {
if (mCallback != null) {
// 这种方法允许让Activity等来实现Handler.Callback接口
// 避免了自己定义Handler重写handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
// 根据Handler子类实现的回调方法处理消息
handleMessage(msg);
}
}
// 处理Runnbale类消息的方法
private static void handleCallback(Message message) {
// 直接调用Message中封装的Runnable的run方法
message.callback.run();
}
// 由Handler子类实现的回调方法
public void handleMessage(Message msg) { }
通过上面对 Handler 核心源码的分析,我们可以看到其在异步线程消息处理机制中的重要作用。任何持有 Handler 引用的其他线程都可以发送消息,这些消息都发送到 Handler 内部关联的 MessageQueue 中,而 Handler 是在 Looper 线程中创建的(必须在 Looper.prepare()
调用之后,Looper.loop()
调用之前创建),所以 Handler 是在其关联的 Looper 线程中处理取到的消息。
正是由于 Handler 这种机制解决了 Android 在非主线程(UI 线程)更新 UI 的问题。由于 Android 主线程也是一个 Looper 线程,在主线程创建的 Handler 默认将关联到主线程的 MessageQueue。我们可以在 Activity 中创建 Handler,并将其引用传递给工作线程,在工作线程执行完任务后,通过 Handler 发送消息通知 Activity 更新 UI。
Message
在消息处理机制中,Message 扮演的角色就是消息本身,它封装了任务携带的额外信息和处理该任务的 Handler 引用。
Message 虽然有 public 构造方法,但是还是建议使用 Message.obtain()
方法从一个全局的消息池中得到空的 Message 对象,这样可以有效的节省系统资源。Handler 有一套 obtain 方法,其本质是其实是调用 Message 的一套 obtain 方法,最终都是调用 Message.obtain()
。
另外可以利用 message.what
来区分消息类型,以处理不同的任务。在需要携带简单的 int 额外信息时,可以优先使用 message.arg1
和 message.arg2
,这比用 Bundle 封装信息更省资源。
MessageQueue
MessageQueue.enqueueMessage()
这个方法是所有消息发送方法最终调用的终点,也就是说无论怎么发送消息,都会直接插入到对应的消息队列中去。并且在插入后还会根据一些判断,来决定是否唤醒阻塞的队列。
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
// 如果发送消息所在的线程已经终止,则回收消息,返回消息插入失败的结果
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) {
// 队列里无消息,或插入消息的执行时间为0(强制插入队头),或插入消息的执行
// 时间先于队头消息,这三种情况下插入消息为新队头,如果队列被阻塞则将其唤醒
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;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
// Native方法唤醒等待的线程
nativeWake(mPtr);
}
}
return true;
}
MessageQueue.next()
该方法可以从消息队列中取出一个需处理的消息,在没有消息或者消息还未到时时,该方法会阻塞线程,等待消息通过 MessageQueue.enqueueMessage()
方法入队后唤醒线程。
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 通过native层的MessageQueue阻塞nextPollTimeoutMillis毫秒时间
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 尝试检索下一个消息,如果找到则返回该消息
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 被target为null的同步消息屏障阻塞,查找队列中下一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 下一条消息尚未就绪。设置超时以在准备就绪时唤醒
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 从队列中获取一个要执行的消息
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 {
// 队列中没有消息,一直阻塞等待唤醒
nextPollTimeoutMillis = -1;
}
...
// 如果第一次遇到空闲状态,则获取要运行的IdleHandler数量
// 仅当队列为空或者队列中的第一条消息(可能是同步屏障)
// 还没到执行时间时,才会执行IdleHandler
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 如果没有IdleHandler需要执行,那么就阻塞等待下一个消息到来
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 运行IdleHandler
// 执行一次next方法只有第一次轮询能执行这里的操作
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 {
// 执行IdleHandler,返回是否保留IdleHandler
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
// 如果不需要保留,则移除这个IdleHandler
mIdleHandlers.remove(idler);
}
}
}
// 设置为0保证在再次执行next方法之前不会再执行IdleHandler
pendingIdleHandlerCount = 0;
// 当调用一个IdleHandler执行后,无需等待直接再次检索一次消息队列
nextPollTimeoutMillis = 0;
}
}
上面源码里面的方法 nativePollOnce(ptr, nextPollTimeoutMillis)
是一个 Native 方法,实际作用就是通过 Native 层的 MessageQueue 阻塞 nextPollTimeoutMillis 毫秒的时间。
- 如果 nextPollTimeoutMillis = -1,一直阻塞不会超时。
- 如果 nextPollTimeoutMillis = 0,不会阻塞,立即返回。
- 如果 nextPollTimeoutMillis > 0,最长阻塞 nextPollTimeoutMillis 毫秒(超时),如果期间有程序唤醒会立即返回。
Q&A
1. ThreadLocal 是什么?
2. post
和 sendMessage
两类发送消息的方法有什么区别?
3. 为什么要通过 Message.obtain()
方法获取 Message 对象?
4. Handler 实现发送延迟消息的原理是什么?
5. 为什么主线程不会因为 Looper.loop()
里的死循环卡死?
6. 同步屏障 SyncBarrier 是什么?有什么作用?
7. IdleHandler 是什么?有什么作用?
8. HandlerThread 是什么?
9. 能否在子线程更新 UI ?为什么 onCreate()
中的子线程更新 UI 有时可以成功?
10. 为什么非静态类的 Handler 导致内存泄漏?如何解决?
参考资料
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message 三者关系
Android 异步消息处理机制完全解析,带你从源码的角度彻底理解
Android 消息处理机制(Looper、Handler、MessageQueue、Message)