Handler,Message,Looper,MessageQueue Android 异步消息机制
我们都知道,使用 Handler 能够在 UI 主线程和子线程之间进行通信,最简单的代码如下:
public class BitmapActivity extends Activity implements Handler.Callback {
private Handler handler = new Handler(Looper.getMainLooper(), this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bitmap_size_test);
testHandler();
}
private void testHandler() {
new Thread(new InnerThread()).start();
}
public static final int NOTIFY_CODE = 100;
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case NOTIFY_CODE:
Toast.makeText(this, "子线程消息", Toast.LENGTH_SHORT).show();
break;
}
return false;
}
public class InnerThread implements Runnable {
@Override
public void run() {
int toastCount = 0;
try {
while (toastCount < 10) {
Thread.sleep(1 * 1000);
handler.removeMessages(NOTIFY_CODE);
handler.sendEmptyMessage(NOTIFY_CODE);
toastCount++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
子线程发送消息到主线程,然后主线程 Toast 一次。
要想知道,Handler 和 Message 和 Looper 之间的关系,还是需要从类解析说起。
相关类解析
Message 类
Message 类其实就是一个 信息类,类似于我们常用的 Java Bean 类,其继承关系如下
public final class Message implements Parcelable {}
根据官方文档的解释,Message 类包含了内容和任意数据,可以被 Handler 发送和传递。它的数据域包含两个 int 类型参数和一个 Object 类型的参数,可以被作为数据传递:
Message 中关键数据域说明如下:
//传递的参数
public int arg1;
public int arg2;
public Object obj;
//--------
Message next;//下一条 Message
Message sPool;//当前 Message
Handler target;//处理该 Message 的目标 Handler
Runnable callback;// Message 对应的 Runnable,响应改 Message,run() 方法执行改 Message 的响应,可能为null
获取一个 Message
虽然 Message 的构造方法是 public 的,但是官方建议使用以下两个方法获取一个 Message,达到复用的目的:
- Message.obtain()
- Handler.obtainMessage()
其中,2 也是直接调用了 1。
MessageQueue 类
根据官方 API 的描述,Message Queue 保存了通过 Looper 发送的 Message。Message 不是直接加入到 messageQueue 中的,而是通过和 Looper 关联的 Handler。
那么一个 Message 如何被加入到 MessageQueue 中的呢?通常我们会调用 sendMessage() 方法发送一条 Message。
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);
}
最终调用的是 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() 方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;//当前 Message,如果第一次 enqueue,这个对象为 null
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//当前 Message 为空,when =0,或者 插入 Message 的when 小于链表头的 Message 的when
// New head, wake up the event queue if blocked.设置链表头
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;//遍历链表插入合适的位置
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {//遍历到链表末尾或者找到 when 小于的位置
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
而每个 Looper 里面都会创建一个 MessageQueue ,在 Looper 的构造方法中:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
每一个 Thread 中,存在一个 Looper,每个 Looper 创建的时候会同时创建一个 MessageQueue。这里再回过头来看一下获取 Message 的方法。
private static Message sPool;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
这里会通过缓存的 static Message 变量 sPool ,去获取一个 Message。那么这个 sPool 又是在哪里被赋值的呢?在 Message 的 recycleUnchecked() 方法里面:
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//这些操作是将 Message 对象里面的数据初始化,使 Message 可以被复用
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
//注意了:这里会重新构建一个链表,每次新的 Message 会指向 sPool,上次的回收的 message 指向 next
//也就是说,这个缓存池列表,第一个被取出来的是,最后一个放进去的
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
这个方法在 MessageQueue 的 removeMessage() 系列方法会被调用,也是就是回收的 Message 都是被 remove 调的。那么正常出队的 Message 呢?我们看一下 Looper.loop() 方法:
public static void loop() {
..........
for (;;) {
Message msg = queue.next(); // might block
.........
msg.recycleUnchecked();
}
}
所以正常出队的 Message,也是会被回收利用的。
Looper 类
根据官方的解释, Looper 类用于执行一个 Thread 里面的消息循环,子线程默认是不会创建 Looper 的,你需要调用 Looper.prepare() 去为该线程创建一个 Looper,并且调用 loop() 方法开启循环,同时在线程销毁前,调用 quit() 方法,停止该 Looper 的loop();
你可以这样理解, Looper 就是为该线程创建了一个消息队列,同时不停的在访问这个消息队列,并根据消息的 Target,也就是 Handler 对象,将该消息分发出去。
关键数据域
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class //UI 线程looper 由系统创建
final MessageQueue mQueue;//该 Looper 对应的消息队列
final Thread mThread;
这里关键看一下 ThreadLocal 对象,ThreadLocal 保证了每个线程持有的对象实例是唯一的,所以,在不同的线程之间,对应的不同的唯一 looper。
Looper.prepare()
子线程使用 Looper 需要先调用 prepare()方法,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));
}
在调用 prepare() 方法的时候,会创建一个新的 Looper 对象,并且放置在 ThreadLocal 对象中。
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 traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);//关键代码,分发 Message
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();//调用 Message 的recycleUnchecked() 方法,达到复用该 Message 的目的
}
}
这里方法比较简单,就是在死循环里面不断的取出消息,然后分发,分发的关键代码是下面这一行:
msg.target.dispatchMessage(msg);//分发 Message
Handler 类
Handler 作为 android 消息机制里面,我们直接使用也是最常使用的组件,虽然用法简单,但是涉及的原理还是挺多的。
根据官方文档的解释,Handler 类 用于发送和处理 线程对应的 Message Queue 关联的Message 或者 Runnable。每一个 Handler 都指定仅有一个线程,当你创建 Handler 的时候,会绑定到当前线程。
构造方法
Handler 的构造方法比较多,但是最终无非调用这几个
//第一类构造方法,不指定 Looper,使用主线程的 Looper
public Handler() {
this(null, false);
}
public Handler(boolean async) {
this(null, async);
} public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();//获取的是 UI 主线程的 Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//第二类构造方法,指定 Looper
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
其实都很简单,都是对 Looper Callback, mAsynchronous 进行赋值而已。
sendMessage() 系列方法
sendMessage() 系列的方法上面讲解了,最终会调用 enqueueMessage() 方法,把 Message 插入消息链表中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
post() 系列方法
post() 系列方法,会把 Runnable 封装进 Message ,然后同样调用的是 sendMessage() 方法,源码如下:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//将 Runnable 封装到 Message 中
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
最终响应该 Message 的操作是在 dispatchMessage() 方法,调用时机看 Looper.loop() 方法。
dispatchMessage() 方法
响应该 Message 的操作是在 dispatchMessage() 方法,调用时机看 Looper.loop() 方法,其源码如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
所以,到这里很清楚的了解到,一个 Message 如何被产生的,如果被发送,插入到消息队列的,并且最终被出队消费的。
总结
这里再来总结一下,并且画一下流程图:
- Message.obtain() 获取一个 Message
- Handler.sendMessage()
- Handler.enqueueMessage() 将 Message 插入消息队列(链表结构)
- Looper.loop() 循环取出消息
- Handler.dispatchMessage() 分发消息
- Callback 或者 handleMessage() 消费该消息
- 回收复用该消息,即执行 recycleUnchecked() 方法
UI 主线程的消息机制
子线程中Looper 是需要手动调用 prepare() 方法的,并且需要调用 Looper.loop()。那么 UI 主线程中,系统会帮你创建Looper。创建代码在 ActivityThread 的 main() 方法里面:
public final class ActivityThread {
public static void main(String[] args) {
.......
Looper.prepareMainLooper();
.......
Looper.loop();
........
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
我们可以看到,一启动应用的时候,就会调用 prepareMainLooper() 方法创建 UI 主线程对应的 Looper,并且调用 loop() 方法,同样在杀死进程的时候,会调用 quit() 方法,退出 Looper,如下:
public void handleMessage(Message msg) {
switch (msg.what) {
......
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
...........
}
}
好的,全部讲解这里应该很清晰了,再来谈一下实际开发中,延伸的两个问题吧。
android 消息机制的延伸问题
这里主要是关注下,实际开发中,需要注意的问题,涉及性能,使用习惯等问题。
remove()
Handler 里面提供了一系列的 remove() 方法,这里分三类,一类是 removeMessage() 方法,用于移除普通的 Message,第二种是 removeCallbacks() 方法,用于移除 Runnable,第三种是根据 Message
.obj 移除响应的 Message 或者 Runnable,如果传入 null,则移除该 Handler 关联的所有 Message 和 Runnable。
三种对应的 MessageQueue 中的方法如下:
removeMessage()
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.如果需要移除的 Message 是链表头
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.移除的 Message 非链表头
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
removeCallbacks()
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.如果需要移除的 Message 是链表头
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.移除的 Message 非链表头
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
removeCallbacksAndMessages()
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
这里由于没有对比 Message.when 或者 Message.callback ,如果传入的 Object 对象为 null,则该 Handler 关联的 MessageQueue 里面的所以 Message 都会被移除,因为所以都满足条件。
Handler 机制检测UI 主线程卡顿
我们在 Looper.loop() 方法里面注意到有这样几行代码
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
.......
try {
msg.target.dispatchMessage(msg);
} finally {
........
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
在调用 Handler.dispatchMessage() 之前,有个 logging 变量先后调用了 println 方法,利用这一点机制,我们可以检测,那些阻塞 UI 主线程的方法。
Github 上也有个相关的框架,以前叫 BlockCanary 的,现在改名了