源码解读 Android 消息机制( Message MessageQueue Handler Looper)2
Looper
前面介绍了 Android 消息机制中消息和消息队列,有了传递的消息和存储的队列,接下来我们结合源码了解下进行调度的 Looper。
官方文档对 Looper 的介绍:
Looper 是用于运行一个线程中的消息的类。
Looper 的属性很简单:
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // 主线程中的 Looepr
final MessageQueue mQueue; //与之管理的消息队列
final Thread mThread; //所在的线程
private Printer mLogging;
private long mTraceTag;
线程中默认没有 Looper,我们需要调用Looper.prepare()
方法为当前线程创建一个 Looper,然后就可以调用loop()
方法调度消息。
样例代码如下:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
线程相关 ThreadLocal
前面讲了在一个线程中需要调用Looper.prepare()
方法创建一个 Looper:
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。
创建一个或者或许当前线程的 Looper 都通过ThreadLocal
,我们来了解下它的主要源码。
ThreadLocal.get()
和ThreadLocal.set()
源码:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocalMap
中持有一个Entry
的数组:
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal> {
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
可以看到,ThreadLocal
先通过当前线程获取ThreadLocalMap
,然后在ThreadLocalMap
中保存ThreadLocal
和 数据的关联。
也就是一个类似 Map
无限循环调度
在线程中创建一个Looper
以后,就可以调用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();
if (me == null) { //当前线程必须创建 Looper 才可以执行
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
//底层对 IPC 标识的处理,不用关心
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { //无限循环模式
Message msg = queue.next(); //从消息队列中读取消息,可能会阻塞
if (msg == null) { //当消息队列中没有消息时就会返回,不过这只发生在 queue 退出的时候
return;
}
//...
try {
msg.target.dispatchMessage(msg); //调用消息关联的 Handler 处理消息
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//...
msg.recycleUnchecked(); //标记这个消息被回收
}
}
可以看到,Looper.loop()
也很简单,就是调用MessageQueue.next()
方法取消息,如果没有消息的话会阻塞,直到有新的消息进入或者消息队列退出。
拿到消息后调用消息关联的 Handler 处理消息。
可以看到,Looper 并没有执行消息,真正执行消息的还是添加消息到队列中的那个 Handler,真应了那句:解铃还须系铃人啊!
如何停止
loop()
源码中的注释就提醒我们,开启循环调度消息后不要忘记调用quit()
方法结束循环。
Looper.quit() 和 quitSafely ()
源码:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
两种退出方式都调用的是
MessageQueue.quit(boolean)
方法:
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) { //`如果链表头部的消息执行时间在将来(也就是一时半会儿没有任务可执行)
removeAllMessagesLocked(); //就直接强硬的全部回收了
} else {
Message n;
for (;;) { //否则找到那个执行时间大于现在的消息,把它后面的消息都回收了
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) { //挨个遍历链表,把消息都回收了
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
可以看到 Looper 两种结束方式的区别:
quit()
:立即把消息链表中的所有消息都回收了,比较强硬- 在停止后如果 Handler 还发送消息,会返回 false,表示入队失败
- 这个方法是不安全的,一般建议使用下面那个
quitSafely()
:比上面那个温柔一点- 只会将还未执行的消息回收掉
- 在调用之后添加进入的消息不会被处理,Handler.sendMessage 也会返回 false
当消息队列被标记位退出状态时,它的next()
方法会返回 null,于是Looper.loop()
循环就结束了。
Handler
现在我们了解了消息队列和 Looper,接着就该介绍消息机制中最关键的角色 – Handler。
每一个 Handler 都和一个线程的 Looper 以及这个线程中的消息队列关联。
Handler 所做的就是“线程切换”:
- 在子线程将 Message 或者 Runnable 发送到 MessageQueue 中
- 然后等待 Looper 调度这个消息后,再召唤 Handler 来处理消息
- 这时消息已经在创建 Handler 的线程了
这个“线程切换” 是怎么实现的呢?我们一步步揭晓。
Handler 的属性
Handler 的属性如下:
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
可以看到 Handler 的属性很简单,其中 mCallback 可以作为构造函数的参数用于新建 Handler。
public Handler(Callback callback) {
this(callback, false);
}
public interface Callback {
public boolean handleMessage(Message msg);
}
我们平时创建 Handler 都是创建一个 Handler 的子类然后重写它的 handleMessage 方法,有了
Handler.Callback
接口我们可以用这种方式创建 Handler:
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//这里处理消息
return false;
}
});
最终效果和创建 Handler 子类一样,但是需要注意,这里创建了匿名内部类,还是会持有外部引用,导致内存泄漏。
发送消息
Handler 发送的主要有两种类型:
- Message
- Runnable
发送方法有postXXX()
和sendXXX()
两种,postXXX 发送的是 Runnable,调用的也是 sendXXX,而 Runnable 也会被转成 Message:
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;
}
所以我们直接看 sendXXX 方法:
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) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到 Handler 发送消息最后还是调用了消息队列的enqueueMessage()
方法。
而我们常用的使用Handler.sendMessageDelayed()
发送延迟消息,最后其实是在入队时指定这个 msg.when,在MessageQueue.next()
方法中,会对 msg.when > now 的消息设置延迟处理,具体实现是在 Native 层。
消息入队后,Looper 如果启动了就可以从队列里循环取消息,然后调用msg.target.dispatchMessage(msg)``` 也就是
Handler.dispatchMessage()“ 方法处理消息。
处理消息
/**
* 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 void handleMessage(Message msg) {
}
可以看到,Handler 在处理消息时,会有三种情况:
msg.callback 不为空
- 这在使用
Handler.postXXX(Runnable)
发送消息的时候会发生 - 这就直接调用 Runnable 的 run() 方法
- 这在使用
mCallback 不为空
- 这在我们使用前面介绍的 Handler.Callback 为参数构造 Handler 时会发生
- 那就调用构造函数里传入的
handleMessage()
方法 - 如果返回 true,那就不往下走了
最后就调用
Handler.handleMessage()
方法- 这是一个空实现,需要我们在 Handler 子类里重写
至此 Handler 发送和处理消息的源码我们就了解了。
移除消息
最后再看一下如果移除消息。
由于发送时可以发送 Callback 和 Message,所以取消也有两种:
- removeCallbacks()
- removeMessages()
看一下源码发现调用的其实就是消息队列的出队方法:
public final void removeCallbacks(Runnable r){
mQueue.removeMessages(this, r, null);
}
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
主线程消息机制
我们知道,在主线程中创建 Handler 时不用 papare Looper,这是因为在主线程中系统默认创建了 Looper,它在不停地调度分发消息,因此四大组件的调度、我们的输入事件、绘制请求才能得到处理。
ActivityThread 就是我们说的主线程,而它的main()
方法就是当主线程的入口:
public static void main(String[] args) {
/...
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
}
可以看到在主线程的main()
方法中初始化了一个 Looper,然后调用了Looper.loop()
方法开始调度消息。
发送和处理主线程中的消息的 Handler 叫做 H:
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
//...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
//...
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
H 的代码很多,我们只节选一部分。从上述代码可以看到,H 内部定义了消息类型,然后根据消息的类型进行不同的处理。
主线程的消息机制如下:
- ActivityThread 通过 ApplicationThread 与 AMS 进行进程通信
- AMS 以进程通信的方式完成 ActviityThread 的请求后回调 ApplicationThread 的 Binder
- 然后 ApplicationThread 向 H 发送消息,H 将消息切换到主线程,然后进行处理
总结
这篇文章结合源码完整的看了一遍 Message MessageQueue Handler Looper,现在看着上面的图,可以自信地说我“熟悉 Android 消息机制”了哈哈。
结合Android 性能优化:多线程理解会更深一些!
Thanks
《Android 开发艺术探索》
http://book2s.com/java/src/package/android/os/handler.html#d0bdd75e69340e6c9e876ee32501beae
http://book2s.com/java/src/package/android/os/looper.html
http://book2s.com/java/src/package/android/os/message.html