概述
Android 的消息机制主要是指 Handler 的运行机制。Android 规定只有主线程可以访问 UI ,子线程中无法访问 UI。但是主线程中不建议进行耗时操作,因为这会引起 ANR。
系统为什么不允许子线程中访问 UI?
如果多线程并发访问,UI 控件处于不可控制的状态。如果对 UI 控件的访问上锁,首先上锁机制会让 UI 访问的逻辑变得复杂;其次会降低 UI 的访问效率,因为锁机制会阻塞某些线程的执行。所以,采用单线程模型来处理 UI 操作简单高效。
消息机制解决了我们需要从服务器获取数据后操作 UI 的矛盾。但是更新 UI 仅仅是 Handler 的一个使用场景。本质上来说,Handler 并不是专门用于更新 UI 的,它只是在更新 UI 的场景中用的比较多。Handler 的使用过程很简单,通过它可以轻松的将一个任务切换到 Handler 所在的线程中去执行。Handler 的运行需要底层的 MessageQueue 和 Looper 的支撑。
Android消息机制有哪些核心成员组成的呢?
- MessageQueue:
消息队列。 它的内部存储了一组消息,以队列的形式对外提供插入和删除的操作。但是内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。 - Looper:
消息循环。以无限循环的形式去查询是否有新消息,如果有的话就处理消息,没有就一直等待。 - Handler:
消息传递的主要操作者。将消息从一个线程传递到另外一个线程。 - ThreadLocal:
不是线程,它的作用是可以在每个线程中存储数据。
Handler 创建的时候会采用当前线程的 Looper 来构造消息循环系统,Handler 内部要使用 ThreadLocal 获取当前线程的 Looper。ThreadLocal 可以在不同的线程中互不干扰的存储并提供数据,通过 ThreadLocal 可以轻松获取每个线程的 Looper。
注意:线程默认是没有 Looper 的,如果需要使用 Handler 必须为线程创建 Looper。主线程(UI线程 ActivityThread)被创建时就会初始化 Looper,这也是主线程中默认可以使用 Handler 的原因。
Handler分析
Handler 是Android消息机制的上层类,也是Android消息机制中我们接触最多的类。使用消息机制的时候,我们都是通过 Handler 的 post 和 send 系列方法开始的,我们从这里开始分析,借助源码,探索Android消息机制的底层原理。我们先看 post 系列方法源码:
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 postAtTime(Runnable r, Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
我们再看 send 系列方法的源码:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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);
}
画个简易的图,理清这些方法之间的关系。
图中可以发现:post 系列和 send 系列的方法最终都指向了 sendMessageAtTime()方法。也就是说,send 系列的方法最终都是靠 sendMessageAtTime()方法实现的。我们看下 sendMessageAtTime 方法的实现:首先获取一个 MessageQueue,然后传给 enqueueMessage 方法,调用此方法。下面是 enqueueMessage 方法源码:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,最后执行的是 MessageQueue 类的 enqueueMessage 方法。我们知道 MessageQueue有两个很重要的方法:enqueueMessage 和 next。enqueueMessage 负责往消息队列里添加数据,next 负责从消息队列里取出数据。所以,handler post 和send 系列的方法做的事情就是往 MessageQueue 里面添加数据。
现在我们知道数据是怎么添加到消息队列里面去的了,这是消息传递的第一步,那第二步就是把消息取出来进行处理,虽然处理消息依然是 handler 的事,但把消息取出来却是 Looper 默默一直干的事。我们分析下 Looper。
Looper
Looper 是让我们整个消息机制循环起来的核心类。普通的线程是没有消息队列的,也是无法使用 Handler 的(主线程:ActivityThread 被创建的时候默认初始化 Looper ,这也是我们可以直接在主线程使用 Handler 的原因)。正是借助 Looper 让线程成为 Looper 线程,线程和 Looper 绑定后,也就有了消息队列,因为消息队列 (MessageQueue) 是放在 Looper 类里面的。那么我们怎么做可以让普通线程变成可以使用消息机制的线程呢?很简单,使用 Looper 类的两个静态方法:
- Looper.prepare()
- Looper.loop()
这两个方法具体做了什么呢?我们看下源码:
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));
}
prepare 方法很简单,里面就做了一件事,就是给变量 sThreadLocal 设置值。sThreadLocal 变量是一个 ThreadLocal 类型的变量:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
如果对 ThreadLocal 不太了解可以先看下这篇文章 Android消息机制-ThreadLocal。我们知道,ThreadLocal 的作用就是保证使用 ThreadLocal 存储的数据只属于当前线程,其他线程无法获取。sThreadLocal 泛型是 Looper,所以这个变量会存储一个 Looper 对象。prepare() 方法是调用了 sThreadLocal 的 set 方法存储了一个新建的 Looper 对象。存储之前会判断是否已经有了 Looper 对象,如果已经有了,会抛异常。所以,一个线程中只能有一个 Looper 对象。这样就保证了这个 Looper 对象只属于当前线程,而且只有一个 Looper 对象。我们看看 new Looper(quitAllowed)这步都干了什么:
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,首先,构造方法是私有的,就是在别的类中不能实例化 Looper 对象。方法中就是给两个变量赋值,一个是我们的消息队列 mQueue,一个是线程对象 mThread。此时,我们的 Looper 对象有了消息队列,而且获取到了当前线程。然后看下 loop() 方法:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();//1、获取looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//2、获取消息队列
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // 取出队列中的消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);//消息不为空执行此处
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
这里就出现了我们一直想寻找的东西啦。首先 1 处通过 myLooper()方法获取到存储的 looper 对象:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
然后 2 处通过获取到的 Looper 对象获取到对象中的消息队列。获取到消息队列后,通过一个不设参数的 for 循环方法不断取出消息,如果消息不为空,就执行:
msg.target.dispatchMessage(msg);
msg我们都知道是 Message,那么 msg.target 是什么呢?哈哈,还记得我们前面分析的 Handler 把消息存入消息队列的过程吗,Handler 的存储方法:enqueueMessage 方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//这里把handler对象赋给了target
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
所以,这个 target 就是我们的 handler 对象,next 方法取出消息后就调用了我们 handler 对象的 dispatchMessage() 方法。这里就是我们能不断处理消息的关键: Looper 对象一直在幕后不断的取出消息给我们的 handler 对象,然后由 handler 对象去处理消息。
现在我们已经清楚了消息队列是什么时候构造的,消息是什么时候存入队列的,消息是怎么取出的,还差一步,我们看看 handler 是怎么处理消息的,就是我们上面取出消息后执行的方法:dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先判断 msg 的 callback 是否为空,callback 是什么呢?是我们调用 post(Runnable r) 方法传入的 runnable 对象:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
不为空的时候用的是 post 系列方法,如果为空则用的是 send 系列方法。第二步,判断 mCallback 是否为空,mCallback 又是什么呢?
final Callback mCallback;
public interface Callback {
public boolean handleMessage(Message msg);
}
看到这里,大家应该明白了吧,终于看到我们熟悉的处理消息的方法了:handleMessage。mCallback 是否为空对应的是 Handler 类的不同构造方法。
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
我们在创建 handler 对象的时候,可以直接传入一个匿名内部类去实现 handleMessage 方法;也可以构造方法不传参,然后去实现 handlerMessage 方法。所以,在 dispatchMessage 方法中,判断如果 mCallback 为空的话,执行 handleMessage 方法。这样,我们就走到了 handleMessage 方法,就可以按照我们的业务逻辑去处理消息了。
最后画一张图总结一下整个流程: