Handler是Android消息机制的核心,但是要分析Handler的原理离不开MessageQueue和Looper。所以三者要一起分析。待解决问题:为什么在子线程中执行new Handler()会抛出异常?
先看图
这是描述Android消息机制的一张图,每个线程有一个Looper和一个MessageQueue,以及若干个Handler。其中MessageQueue存放了一系列Message的队列,而每个Handler通过sendMessage
方法发送一个Message到MessageQueue中,并且这个Message持有发送它的Handler的引用。Looper通过Looper.loop()
方法不断地从MessageQueue中取出Message,并将取出来的Message交给它持有的Handler对象,这个Handler对象通过handleMessage
方法处理这个Message。
关键在于,Handler的handleMessage
一定会在创建它的线程的上下文中被调用,但是它的sendMessage
可以在其他线程中被调用,因此通过Handler可以实现跨线程通信。
首先举一个Android消息机制的例子来引入主题。
Android中常用handler更新UI,在主线程实例化handler,实现它的handleMessage()
方法,为UI控件添加一个点击事件,在子线程中实例化一个Message对象,然后用handler发送它,handler在主线程中接收这个Message对象并更新UI。
public class HandlerActivity extends BaseActivity implements View.OnClickListener {
private Button mBtnChangeUI;
private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.module_activity_handler);
mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 0:
mBtnChangeUI.setText(new StringBuilder(mBtnChangeUI.getText()).reverse());
return true;
}
return false;
}
});
mBtnChangeUI = findViewById(R.id.btn_changeUI);
mBtnChangeUI.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_changeUI:
new Thread(new Runnable() {
@Override
public void run() {
mHandler.sendEmptyMessage(0);
}
}).start();
break;
}
}
}
让我们看看这里发生了什么。首先我们在UI线程里创建了mHandler,并实现了它的handleMessage()
方法,然后实现点击事件,当Button被点击时,创建一个字线程,在子线程里用mHandler发送一个Message,随后倒置Button上的字符串。
结合上面的图,在主线程中创建mHandler,于是mHandler是主线程的,在子线程中发送一个Message,将其放入MessageQueue,这个MessageQueue应该和mHandler在同一个线程,所以也是主线程的,与mHandler同样位于主线程的Looper不断的从MessageQueue中取出Message,并将其交给Message持有的Handler也就是mHandler处理,mHandler在handleMessage
中处理Message,将Button上的字符串倒置。
在代码中看不到Looper和MessageQueue,因此要查看源码(注:为了简洁,省略了部分源码)。
首先,看Handler的创建过程:
Handler的构造函数
Handler有多个构造函数,这里使用的是
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
// ...
mLooper = Looper.myLooper();
// ...
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到这里出现了Looper,mLooper是Handler的一个实例变量,这里的mQueue也是实例变量,而且是MessageQueue类型的,因此Handler持有Looper和MessageQueue的引用。同时Looper也持有MessageQueue的引用。如图所示:
Handler一共有七个构造函数:
public Handler();
public Handler(boolean async);
public Handler(Callback callback);
public Handler(Callback callback, boolean async);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);
public Handler(Looper looper, Callback callback, boolean async);
但是它们之间会互相调用,因此最终起作用的只有两个构造函数:
public Handler(Callback callback, boolean async);
和public Handler(Looper looper, Callback, callback, boolean async);
前者通过Looper.myLooper()
获取Looper,后者手动指定Looper。MessageQueue和Looper是绑定的,制定了Looper也就指定了MessageQueue。
观察Handler的两类方法:发送消息
和处理消息
Handler发送消息的方法
发送消息又有七个方法:
public final boolean sendEmptyMessage(int what);
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis);
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendMessage(Message msg);
public final boolean sendMessageAtFrontOfQueue(Message msg);
public boolean sendMessageAtTime(Message msg, long uptimeMillis);
public final boolean sendMessageDelayed(Message msg, long delayMillis);
同样的,它们之间互相调用,最终起作用的是两个方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, 0);
}
我们发现发送消息
最终就是调用了enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
这个方法,顾名思义,这是一个Message入队的方法,它的代码如下:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里,Message获得了Handler的引用,message.target = handler
。而queue是一个MessageQueue变量。这正是——Message被其持有的Handler发送,插入到MessageQueue。
MessageQueue的enqueueMessage
方法后面再看。记录1⃣️
Handler处理消息的方法
/**
* Handle system messages here.
*/
public void dispatchMessage(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();
}
public interface Callback {
public boolean handleMessage(Message msg);
}
public void handleMessage(Message msg) {}
除了常用的覆写handleMessage
接口的方法实现处理消息的方法之外,还有两种方法,使用Message自带的callback,或者传入一个Callback实例。代码很清晰。传入Callback实例的方法是采用Handler构造方法中的public Handler(Callback callback, boolean async);
或public Handler(Looper looper, Callback callback, boolean async);
。
Message的callback变量后面再看。记录2⃣️
综上,Handler的常用方法讲完了,现在解决记录1⃣️。分析MessageQueue的源码。
1⃣️MessageQueue的enqueueMessage
方法。
MessageQueue
Android的每个线程可以持有一个Looper对象和一个MessageQueue对象,Looper不停地从MessageQueue里取出Message,并将Message交给Message持有的Handler对象来处理,当MessageQueue为空时Looper阻塞。而Message是由其持有的Handler对象插入MessageQueue的。Handler持有它被创建的线程的上下文,并且可以被多线程共享。
MessageQueue是由一个单链表的数据结构实现的,主要包含两个操作:插入和读取,对应的方法为enqueueMessage和next,enqueueMessage的作用是插入一条Message,next的作用是取出一条Message并将其从MessageQueue中移除,先发出的Message靠近队列的首部,现在看这两个方法的源码:
// 插入msg,指定其在when(ms)的延时后触发
boolean enqueueMessage(Message msg, long when) {
//...
// 单链表插入,通过同步来防止多线程冲突
synchronized (this) {
//...
msg.when = when;
Message p = mMessages;
boolean needWake;//需要唤醒的标识
if (p == null || when == 0 || when < p.when) {
// 消息队列为空 || msg是立即触发的消息 || msg比队列首部的消息先触发,则将其插入到队列首部
msg.next = p;
mMessages = msg;
needWake = mBlocked;//
} else {
// 消息队列不为空 && msg比队列首部的消息晚触发 && p不持有Handler && msg是异步消息
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; // 根据触发的时间将msg插入到合适的位置
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
needWake这个变量的用法还不明朗,在省略的源码里也有一些不清楚用法的变量,Message的同步/异步是什么,inUse表示是什么,等等。但是这个方法的主要功能是明显的,向队列插入Message,MessageQueue是单链表实现的,其中的Message按照触发的时间when排序,先触发的排在前面。有入队,就有出队,出队方法是next
。
Message next() {
//...
for (;;) {
//...
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// MessageQueue不为空 && 队首的Message不持有Handler
if (msg != null && msg.target == null) {
// 找到MessageQueue中第一个不为空 && 同步的Message
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//
if (msg != null) {
// 还没到Message触发的时间
if (now < msg.when) {
//...
} else {
//...
// 从队列中移除Message
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//...
return msg;
}
} else {
// No more messages.
//...
}
}
}
}
next
是一个循环函数,在MessageQueue里找到符合条件的Message(条件很复杂),将其从MessageQueue中移除并返回。
接下来分析Looper。
Looper
Looper不停地查看MessageQueue中是否有Message,这是在它的loop
方法中完成的,因此loop
是它的核心方法,从这个方法开始看
public static void loop() {
final Looper me = myLooper();
//...
final MessageQueue queue = me.mQueue;
//...
for (;;) {
// 从MessageQueue中取出Message
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...
try {
msg.target.dispatchMessage(msg);// 使用Message持有的Handler处理事件。
//...
} finally {
//...
}
//...
}
}
可以看到,loop
是一个循环函数,从MessageQueue中不断地取出Message并处理,当MessageQueue为空时退出循环。照例,MessageQueue是与Looper绑定的,通过myLooper
方法获取Looper。查看myLooper
方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
我们发现这里用到了ThreadLocal变量,也就是说每个线程都有一个线程独立的Looper,而他是在Looper.prepare()
方法里获取到的。
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));
}
可以看到每个线程只能有一个Looper。
到这里,我们可以解决开头的问题了——为什么在子线程中执行new Handler()会抛出异常?
在子线程中执行new Handler(),Handler()中需要调用myLooper()
获取Looper,而此时Looper是空值,因此抛出异常。
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//...
}
那么,为什么在主线程中执行new Handler()不会抛出异常呢?回到Looper的源码,可以发现prepare
方法被prepareMainLooper
方法调用了,而prepareMainLooper
方法又被ActivityThread.main
方法调用了。
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 static void main(String[] args) {
//...
Looper.prepareMainLooper();
//...
Looper.loop();
//...
}
因此,在主线程初始化的时候就已经创建了Looper,所以执行new Handler()不会抛出异常,在子线程中就需要手动调用Looper.prepare()
来创建了。主线程的Looper名为sMainLooper,是一个静态私有变量,使用共有静态方法getMainLooper
就可以获取到它,因此在子线程中也可以手动获取主线程的Looper。
顺便看看Looper的构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper在实例化的时候会创建自己的MessageQueue,并获取当前线程的引用。因此每个Looper都有一个MessageQueue。Looper使用单例设计,每个线程只能有一个Looper。子线程中的Looper使用ThreadLocal存储,对其处理时不需要做同步处理,主线程中的Looper是一个类变量,对其处理时需要做同步处理。
Looper不停地查看MessageQueue中是否有Message,如果有新消息就处理,否则阻塞。通过Looper.prepare()
创建Looper。
综上所述,Handler-MessageQueue-Looper的关系就讲完了。再回顾一下Android消息机制是如何实现的:
-
每个线程中都至多只能持有一个Looper,每个Looper持有一个MessageQueue实例和当前线程的引用。主线程在初始化的时候自动调用
Looper.prepareMainLooper
创建了一个MainLooper,对于子线程,需要手动调用Looper.prepare
创建Looper。 -
每个线程可以持有多个Handler,Handler持有一个Looper的引用,可以为Handler指定Looper,也可以让Handler自行获取当前线程的Looper。
-
手动创建Message,使用Handler发送Message,Handler会调用MessageQueue的方法,将Message插入MessageQueue。
-
Looper.loop
,循环从MessageQueue取出Message,然后调用Handler.dispatchMessage()
来处理Message。
参考资料:
《Android开发艺术探索》