Message
Message 是线程之间传递的消息,可以携带少量数据,用于在不同线程之间交换数据。
public 字段 | 含义 | 注释 |
---|---|---|
int what | 用户定义的消息标记 | |
int arg1 | 内置可存放的第一个整型数据 | |
int arg2 | 内置可存放的第二个整型数据 | |
Object obj | 内置可存放的一个对象数据 | |
Messenger replyTo | 可回应的消息的发信人 |
构造方法推荐使用 Message.obtain()
,因为静态工厂有一个可重复利用的对象池。
MessageQueue
消息队列:主要用于存放所有通过 Handler 发送的消息,消息会一直存在于消息队列中,等待被处理。每个线程只会有一个消息队列。
底层为单链表,主要包含两个操作:
enqueueMessage(Message,long)
:插入消息和执行时间。设置消息的 when 为输入的 long 参数,根据执行时间将消息存放在单链表的合适位置。next()
:无限循环的方法,如果队列中没有消息,就一直阻塞,当新消息到来时,就返回这条消息并从单链表中删除。
Looper
每个线程中的消息队列的管家,Looper 的 loop() 方法会进入无限循环中,每当发现消息队列中存在一个消息,就会将它取出,最终传递到 Handler 的 handleMessage 方法中。
static public 方法 | 含义 |
---|---|
prepare() | 为当前线程创建一个 Looper 对象 |
loop() | 当前线程的 Looper 开启消息循环 |
Looper 的 loop()
方法是一个死循环,从 MessageQueue
的 next()
方法中获取新消息,没有就一直阻塞,唯一跳出循环的方式是 MessageQueue 的 next() 方法返回 null。当 Looper 的 quit
或 quitSafely
方法被调用时, Looper 就会调用 MessageQueue 的 quit 或 quitSafely 方法,使得 MessageQueue 的 next 方法返回 null。
获取消息后,Looper 就会处理这条消息:msg.tartget.dispatchMessage(msg)
。这样 Handler 发送的消息最终又交到它的 dispatchMessage
方法。
在子线程中调用 Toast 时,会报错
Can't toast on a thread that has not called Looper.prepare()
解决办法:
Looper.prepare(); // 为当前线程创建一个Looper
Toast.makeText(Page1Activity.this, "content", Toast.LENGTH_SHORT).show();
Looper.loop(); // 开启消息循环
Looper 的 prerare
方法,会通过 ThreadLocal
来检查当前线程是否已经有 Looper 对象了,有的话就会抛出异常,因为一个线程只能有一个 Looper 对象,没有的话就使用 ThreadLocal 给当前的线程添加一个 Looper 对象。
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper 的 loop()
方法,会使用当前线程的 Looper 对象来开启消息循环。
Handler 发送消息原理
Handler 是消息处理器,主要用于发送和处理消息。
public 方法 | 含义 |
---|---|
Message obtainMessage() | 返回一个消息对象,设置其 target 为当前 Handler 实例 |
sendMessage(Message msg) | 用此方法,将其他线程消息的消息发送给 Handler |
handleMessage(Message msg) | 在创建 Handler 的线程中处理收到的消息 |
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
发送消息通过 send 的一系列方法实现,Handler 的 sendMessage 方法会调用 enqueueMessage(queue, msg, uptimeMillis)
方法,设置消息的 target 为当前 Handler 对象,然后将消息插入到消息队列中。
MessageQueue 的 next 方法返回这个消息给 Looper,最终由Handler 的 dispatchMessage
方法来处理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage
方法首先检查消息是否定义了回调函数 callback,定义了的话就调用 handleCallback(msg)
,否则再检查 Handler 是否定义 mCallback,定义了的话就调用 mCallback.handleMessage(msg)
,没有定义 mCallback 或调用 mCallback.handleMessage(msg) 失败才会调用 Handler 的 handleMessage(msg)
。
优先级 | 消息处理函数 |
---|---|
1 | msg.callback.run() 来处理 |
2 | Handler 的 mCallback.handleMessage(msg) |
3 | Handler 的 handleMessage(msg) |
runOnUiThread 实现原理
在非 UI 线程中执行此方法时,会用消息机制来处理 Runnable 对象。Runnable 对象会作为消息的 callback 字段被包装成一个消息 msg,相当于执行了 mHandler.sendMessage(msg)
。
// android.app.Activity
@Override
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
// android.os.Handler
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;
}
活动的主线程是 ActivityThread,mHandler 是每个 Activity 在主线程中创建的消息处理器,因此 Runnable 最终在主线程中运行。
// android.app.Activity
final Handler mHandler = new Handler();
参考资料
- 《Android 开发艺术探索》 任玉刚著
- 《第一行代码 Andriod》 郭霖著