一、消息处理机制的四个组成部分
Android中的消息处理机制主要由四个部分组成:Message、Handler、MessageQueue 和 Looper。
1.Message
Message用于在线程之间传递消息,它可以在内部携带少量的信息。我们常用Message的what字段以及arg1、arg2字段来携带一些整型数据,使用Message的obj字段来携带一个Object对象。
2.Handler
Handler用于发送和处理消息。通过sendMessage()方法发送消息,通过handleMessage()方法处理消息。
3.MessageQueue
MessageQueue是消息队列的意思,它用于存放Handler发送出来的消息。这些消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
4.Looper
Looper是每个MessageQueue的管家,当我们调用了Looper的loop()方法之后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,便会将它取出并传递到Handler的handleMessage()方法中。每个线程中也只有一个Looper对象。
PS:之后我会对它们进行更加详细的讲解,目前只用大致了解概念即可
二、消息处理的简单运用
运用场景:通过点击一个按钮模拟请求网络之后,通过Message通知UI线程更新UI。
1.在子线程中发出Message
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_download:
new Thread(new Runnable() {
@Override
public void run() {
try {
//模拟请求网络下载文件
Thread.sleep(8000);
Message message = new Message();
message.what = DOWNLOAD_TAG;
handler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
break;
default:
break;
}
}
2.在主线程中处理Message
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DOWNLOAD_TAG:
tvDisplay.setText("Download Complete");
break;
default:
break;
}
}
};
PS:也可以使用将异步处理机制封装好的runOnUiThread()方法切换到主线程更新UI:
//模拟请求网络下载文件
Thread.sleep(8000);
runOnUiThread(new Runnable() {
@Override
public void run() {
tvDisplay.setText("Download Complete");
}
});
通过上面这个简单的例子可以看到,我们在主线程中创建了Handler,之后开启子线程处理一些耗时操作,最后发送Message,Handler便会自动地去处理这条消息,然后显示出“Download Complete”的文本。这么一来便实现了跨线程的通讯。但是我们肯定会有很多疑问,比如我们就只看到了Message 和 Handler 不是说有四个组成部分吗?MessageQueue和Looper呢?不是说调用了Looper的loop()方法之后才能处理MessageQueue中的Message吗?我怎么没看到呢?诸如此类的问题可能会有很多,先别急,往下看。
首先,上面的例子比较特殊,是因为Handler创建在主线程,一般的线程默认是没有 Looper 的,如果要使用 Handler,则必须为线程创建 Looper,但是主线程被创建时就会初始化 Looper,因此主线程中默认可以使用 Handler。其次,从开发者的角度来说,Handler 是Android消息机制的上层接口,这使得在开发过程中只需要和Handler交互即可。因此即使不知道MessageQueue和Looper,也不妨碍我们使用它。
话说回来,Android 的消息处理机制主要是指 Handler 的运行机制,Handler 的运行需要底层的 MessageQueue和Looper 的支撑。因此想要了解Android的消息机制也就需要了解MessageQueue和Looper的工作过程。
三、消息处理机制的概述
在讲消息处理机制之前,先解决一个问题:为什么Android系统不允许子线程访问UI?这是因为Android的UI控件不是线程安全的。如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。那么为什么不用同步锁?其原因有二:
- 同步锁会让UI的访问逻辑变得复杂
- 同步锁会降低UI的访问效率
接下来,简单讲下 Handler 的运行机制。Handler 创建完毕后(当然当前线程得先有Looper) ,这个时候其内部的 Looper 以及 MessageQueue 就可以和 Handler开始协同工作了。每当 Handler 发送一个消息,MessageQueue 就会将消息存入队列,而 Looper 作为他们三最累的那个,它会不停地查看队列中有没有消息,如果有新消息了,它便将消息拉出队列,看这个消息是哪个 Handler 发出的,再交由对应 Handler 去处理消息。可能你从上面的描述中看不出来它哪里跨线程了,这是因为实际使用的时候 Handler 的创建和发送消息通常是分别处于不同线程中的。
四、ThreadLocal 的工作原理
ThreadLocal是一个前文未提及的概念,因为就它本身而言,并不局限于消息处理机制。我们先来看看它是怎么进入到我们的视野里的。比如我们要在一个子线程中使用Handler,首先得创建Looper,然后调用其loop()方法,如下:
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
这时我们去看下prepare()的源码,他最终会调用到如下方法:
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。虽然名字里有个Thread 但它不是线程,它是一个线程内部的数据存储类,因此它的作用是可以在每个线程中存储数据。这里的泛型指定的是Looper,因此它所存储的数据也是Looper:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
它神奇的地方在于不同线程对于同一个 ThreadLocal 的变量 sThreadLocal 调用其get()方法获取的数据是不一样的。因此在消息处理机制中,通过 ThreadLocal 可以在不同的线程中互不干扰的获取每个线程的Looper。究其原因,是因为他有一个内部类ThreadLocalMap,而这个map是根据线程的不同而不同的,然后数据是存储在map中的一个数组里面的(table)。ThreadLocal 的 set() 源码如下:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看到它通过getMap()方法根据当前线程去获取对应的map变量,然后将数据存入,如下:
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
ThreadLocal 的 get() 方法也是类似,先根据当前线程获取对应的map,然后从map中取值。如下:
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();
}
五、MessageQueue 和 Looper 的工作原理
当我们通过Handler post 一个 Runnable 或者 send 一个 Message 时,它们最终都会调用 MessageQueue 的 enqueueMessage 方法。而enqueueMessage的实现,主要操作就是单链表的插入操作。
当我们调用Looper.loop()时,其内部会无限循环的调用 MessageQueue 的 next() 方法。而next方法也是一个无限循环的方法,如果消息队列中没有消息,那么next方法便会一直阻塞在这里。当有新消息来到时,next方法便会返回这条消息并将其从单链表中移除。
接着来看Looper,之前说到在一个子线程中使用Handler之前要调用 Looper.prepare() 方法,这个时候是会新建一个 Looper 对象的:
sThreadLocal.set(new Looper(quitAllowed));
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
我们可以看到,在Looper的构造器中,它会创建一个 MessageQueue 即消息队列,然后将当前线程的对象保存起来。接着来看Looper的 loop 方法,只有调用了 loop 后,消息循环系统才会真正地起作用。它的实现如下:
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block 可能阻塞
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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
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();
}
}
可以看到,loop 方法是一个死循环,唯一跳出循环的方式是 MessageQueue 的 next 方法返回了null。当我们去调用 Looper.myLooper().quit() 方法或者 Looper.myLooper().quitSafely() 方法时,它们都会通知消息队列退出,当消息队列被标记为退出状态时,它的next方法便会返回null。因此,当一个子线程的工作结束后,记得退出Looper。接着看loop方法的后续,可以看到它通过msg.target.dispatchMessage(msg) 方法将处理消息的任务交给了 msg.target ,而这就是发送这条消息的Handler对象。如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
六、Handler 的工作原理
Handler 的工作主要包含消息的发送和接收过程。消息的发送可以通过 post 的一系列方法以及 send 的一系列方法来实现,其中post 的一系列方法最终是通过send的一系列方法来实现的。通常发送一条消息都是由 sendMessage 方法开始,经过一系列的委托调用转交给了 enqueueMessage 方法,也就是上一节中最后的那个代码块,可以看到最后 Handler 将工作传递给了消息队列,让它在单链表中插入消息。而这个消息最终会在无限循环的 next 方法中返回给 Looper。然后在上一节的分析中可以看到 Looper 拿到消息之后调用了msg.target.dispatchMessage(msg) 。因此最后又回到了Handler的手上。dispatchMessage的实现如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其处理消息的过程如下:
- 首选判断 Message 的 callback 是否为null,这个callback 实际上是post系列方法所传递的Runnable参数,如果非空,则调用handleCallback进行处理,其实就是调用了Runnable的run方法
- 其次判断 mCallback 是否为 null,如果非空,则调用mCallback 的 handleMessage 方法来处理消息。而 Callback 是一个接口,我们可以用它来创建一个 Handler 对象而不需要派生 Handler 的子类:Handler handler = new Handler(callback)
- 最后调用 Handler 的 handleMessage 方法来处理消息