我们都知道,Android应用程序是通过消息来驱动的。Android应用程序的每一个线程在启动时,都可以首先在内部创建一个消息队列,然后再进入到一个无线循环中,不断检查它的消息队列是否有新的消息需要处理。
如果有新的消息需要处理,那么线程就会将它从消息队列中取出来,并且对它进行处理;
否则,线程就会进入睡眠等待状态,直到有新的消息需要处理为止。这样就可以通过消息来驱动Android应用程序的执行。
Android应用程序的消息处理机制是围绕消息队列来实现的。一个线程拥有一个消息队列之后,就可以进入到一个消息循环中,同时其他线程以及线程本身可以往这个消息队列发送消息,以便可以在这个消息被处理时执行一个特定的操作。
这样我们就将一个线程的生命周期划分为创建消息队列和进入消息循环两个阶段,其中,消息循环阶段又划分为发送消息和处理消息两个子阶段,他们是交替进行的。
Android系统主要通过MessageQueue、Looper、Handler、Message和ThreadLocal五个类来实现Android应用程序的消息处理机制。其中
MessageQueue类:用来描述消息队列,用来存放通过Handler发过来的Message,采用单链表的数据结构来存储消息列表,按照先进先出执行;
Looper类:用来创建消息队列、进入消息循环,以及消息的分发,扮演着Message Queue和Handler之间桥梁的角色;
Handler类:Message的主要处理者,用来发送消息和处理消息;
Message类:Handler接收和处理的消息对象,子线程与主线程之前通信时,相关信息的存放;
ThreadLocal类:线程内部的数据存储类,负责存储和获取本线程的Looper,类似于HashMap。
采用网上的一张图片进行说明一下Android消息机制的大致流程,点开浏览大图,挺详细的
接下来,我们首先分析线程消息队列的创建过程,然后分析线程的消息循环过程,最后分析线程消息的发送和处理过程。
一、创建线程消息队列的过程
Android应用程序线程的消息队列是使用一个MessageQueue对象来描述的,它可以通过调用Looper类的静态成员函数prepareMainLooper()或者prepare()来创建。其中,
prepareMainLooper()用来为应用程序的主线程创建消息队列;
而prepare()用来为应用程序的其他子线程创建消息队列。
先来看看Android程序的入口,其实很多人以为Android的入口是Activity中的onCreate(),其实真正的入口是ActivityThread.main()方法:
public final class ActivityThread {
......
public static void main(String[] args) {
......
Process.setArgV0("<pre-initialized>");
//创建消息的循环器Looper
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();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
主线程创建消息队列的过程,看Looper的源码:
public final class Looper {
// 在主线程创建Looper
public static void prepareMainLooper() {
// 调用prepare(boolean quitAllowed)方法,这个布尔值是告诉你主线程的消息队列是不允许退出的。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException(
"The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
// 往下看
private static void prepare(boolean quitAllowed) {
// 这里要注意,sThreadLocal是用来存放Looper的,这里会判断一下sThreadLocal是否为null,否则抛出异常;
// 也就是说Looper.prepare()或Looper.prepareMainLooper()方法只能被调用一次,即一个线程中只能有一个Looper实例
if (sThreadLocal.get() != null) {
throw new RuntimeException(
"Only one Looper may be created per thread");
}
// 初始化一个Looper对象,并存放到sThreadLocal里面
sThreadLocal.set(new Looper(quitAllowed));
}
// 再往下看
private Looper(boolean quitAllowed) {
// 接下来创建了一个MessageQueue消息队列,即当创建一个Looper的时候,会伴随着创建一个消息队列MessageQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
}
以上是Looper在主线程的初始化,主要做了:
1.初始化一个Looper,随之初始化一个MessageQueue,并关联起来
2.完了将初始化的Looper对象放进ThreadLocal中
注意:在同一线程里面Looper只能被创建一次
子线程创建消息队列:
public final class Looper {
public static void prepare() {
// 同样的调用prepare(boolean quitAllowed)方法,而子线程的消息队列是可以退出的。所以为true
prepare(true);
}
// 往下跟Looper.prepareMainLooper()差不多,参考上面
}
二、消息的循环过程
public final class Looper {
public static void loop() {
// myLooper()方法是返回sThreadLocal中存储的Looper实例,然后判断,如果me为空,就抛出异常
// 也就是说,loop()方法必须在prepareMainLooper()或prepare()方法之后执行,即消息循环必须得先在线程中创建循环器Looper的实例
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException(
"No Looper; Looper.prepare() wasn't called on this thread.");
}
// 获取Looper循环器实例中的消息队列mQueue
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 (;;) {
// next()方法取出消息队列中的消息
// 如果msg为空,那么当前线程就会在MessageQueue类的成员函数next()中进入睡眠等待状态,俗称线程阻塞
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.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
// 消息派发:把消息派发给msg的target属性,然后用dispatchMessage方法去处理
// Msg的target其实就是handler对象
msg.target.dispatchMessage(msg);
} 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();
}
}
}
消息的循环是在这里开始;同时消息的取出也是在这里,下面会分析。这里做了:
1.取出当前线程的Looper对象和MessageQueue对象
2.进入消息的循环过程,有消息就分发出去,没有就等待着(线程阻塞)
3.最后消息资源的回收
另外这里要提一下,Looper的退出有两个方法:
public final class Looper {
public void quit() {
mQueue.quit(false);
}
// 和
public void quitSafely() {
mQueue.quit(true);
}
}
mQueue对象是同样的,实际上都调用了MessageQueue的quit(boolean safe)方法:
public final class MessageQueue {
void quit(boolean safe) {
// 这里判断是不是主线程的Looper,如果是就抛出异常,因为主线程的Looper是不允许退出的
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);
}
}
}
quit():quit会直接清空消息,并退出Looper。实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
quitSafety():当调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
无论是调用了quit方法还是quitSafely方法,Looper将不再接收新的消息。
三、发送消息过程
Handler类内部有mLooper和mQueue两个成员变量,他们分别指向一个Looper对象和一个MessageQueue对象。Handler类还有sendMessage()和handleMessage()两个成员函数。其中,
成员函数sendMessage()用来向成员变量mQueue所描述的一个消息队列发送一个消息;
而成员函数handleMessage()用来处理这个消息,并且它是在与成员变量mLooper所关联的线程中被调用的。
我们一般使用Handler类的默认构造函数创建一个Handler对象,这时候它的成员变量mLooper和mQueue就分别指向与当前线程所关联的一个Looper对象和一个MessageQueue对象,请看Handler源码:
public class Handler {
......
public Handler(Callback callback, boolean async) {
......
//获取当前线程所关联的Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//获取当前线程所关联的MessageQueue对象
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
接下来看看Handler.sendMessage(Message msg)方法,这是发送消息的开始
public class Handler {
......
//Handler类里面的sendMessage成员函数
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
......
//往下看,sendMessageDelayed是指定的消息处理时间是一个相对现在的时间
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
......
//再往下,sendMessageAtTime是指定的消息处理时间是将来的一个绝对时间
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);
}
}
除了成员函数sendMessage之外,Handler类还有另外两个成员函数sendMessageDelayed()和sendMessageAtTime()可以用来向一个线程的消息队列发送一个消息。
成员函数sendMessage发送的消息的处理时间为系统的当前时间。
而成员函数sendMessageDelayed()和sendMessageAtTime()发送的消息的处理时间是将来的一个时间。其中,前者指定的消息处理时间是一个相对现在的时间,而后者指定的消息处理时间是将来的一个绝对时间。
其实Handler还有sendEmptyMessage(int what)方法和post(Runnable r)方法可以发送消息的,其底层的调用和sendMessage(Message msg)一样,只是方式、或者参数有所不同罢了。
public class Handler {
// sendEmptyMessage(int what)方法
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
// post(Runnable r)方法
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;
}
}
接上面的继续往下看,消息到了enqueueMessage()方法中
public class Handler {
......
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//其实就是调用了消息队列MessageQueue的enqueueMessage()方法,往下看
return queue.enqueueMessage(msg, uptimeMillis);
}
......
}
//往下看,这是MessageQueue.enqueueMessage(Message msg, long when)方法
public final class MessageQueue {
......
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;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//三个条件分别是:
//1.目标消息队列是一个空队列
//2.插入的消息的处理时间等于0
//3.插入的消息处理时间小于保存在目标消息队列头的消息的处理时间
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;
//4.插入的消息处理时间大于等于保存在目标消息队列头的消息的处理时间
//而插入的位置将在这个循环中得到
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.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;
}
......
}
这样消息进队列完成,这里做了:
1.目标消息队列是一个空队列,插入的消息的处理时间等于0,插入的消息处理时间小于保存在目标消息队列头的消息的处理时间,并且这时消息队列如果处于等待状态的话则将其唤醒
2.若是在中间插入,则根据Message创建的时间进行插入
这里加个小插曲,简单的说明一下Message这个类,其实这个Message就是用来存储Message中各种信息的Bean对象,从源码中我们可以看看其属性,这里只是列举几个常用的:
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
int flags;
long when;
Bundle data;
Handler target;
Runnable callback;
......
}
另外Message还有一个很有意思的静态方法Message.obtain()。在java中创建和销毁对象是比较小号资源的。如果应用中需要频繁的创建和销毁某个类型的对象,这样会产生很多临时对象,当失去引用的临时对象较多时,JVM会进行垃圾回收(GC),CPU在进行GC时会导致应用程序的运行得不到很好的响应,这时应用程序的响应性就会降低。
而对象池就是为了解决这个问题的,当需要使用Message对象的时候,从对象池中获取;如果sPool == null,那么就返回一个新的Message对象。而对象池的作用是维护对象的生命周期的。
public final class Message implements Parcelable {
......
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();
}
......
}
四、线程消息处理过程
线程的消息需要处理,那么肯定得先将消息取出来,那么哪里取出来呢?
其实Looper.loop()是循环的开始,也是消息被取出的地方。
public final class Looper {
......
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;
......
for (;;) {
//这里是消息取出的地方
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
//上面提过,消息派发:把消息派发给msg的target属性,然后用dispatchMessage方法去处理
//Msg的target其实就是handler对象,下面会继续分析下去
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
}
......
}
先往下看MessageQueue的next()方法:
public final class MessageQueue {
Message next() {
......
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,这时候消息队列处于等待状态。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//按照设定的时间取出消息
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
// 如果消息队列中没有消息,将nextPollTimeoutMillis设为-1,下次循环消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//退出消息队列,返回null,这时候Looper中的消息循环也会终止。
if (mQuitting) {
dispose();
return null;
}
......
}
}
好了取出来之后,我们来看看Handler是如何处理消息的,handler的dispatchMessage(Message msg)
public class Handler {
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//1.这里是post()的消息的处理方式
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//2.这里是sendMessage()的消息的处理方式
handleMessage(msg);
}
}
......
//先来看看post()的消息的处理方式
private static void handleCallback(Message message) {
message.callback.run();
}
......
//sendMessage()的消息的处理方式
public void handleMessage(Message msg) {
}
}
这样消息的处理过程就结束了,这里说明一下最后这里:
1.post()的消息的处理方式是将传进来的Runnable执行run();
2.sendMessage()的消息的处理方式是执行handleMessage(Message msg)这个空方法,这就是为什么要在Handler重写这个方法的原因
结语:
这里的思路还是蛮清晰的:
1.消息队列的创建过程;
2.消息的循环过程;
3.发送消息的过程;
4.处理消息的过程。
在介绍的过程的同时,把Looper、Handler、MessageQueue、TheadLocal和Message的主要部分大致的介绍了一遍,如果大家还想深究,就自己深入的看一下源码。
最后,终于把Android的消息机制理清了一下,因为刚工作了,所以断断续续的理了好几天。这个消息机制在Android开发中占了非常重要的一部分,平时开发中使用的频率是极高的,同时也是面试经常问的。所以不论如何也要把它攻克。虽然乍看起来有点繁杂,但是认真的看几天就会觉得也没多难。