上次文章谈到ThreadLocal的原理。今天接着谈谈Android的消息处理机制。主要使用了四个类,Looper,Message,MessageQueue,Handler。先简单介绍一个这个四个类的作用,再从源码角度去分析。
- Looper
从名字可以猜测,他是不断循环。思考一个问题,在我们new Thread创建一个线程并start之后,在执行完中间的Runnable体之后,线程便结束他的工作,等待被回收。为什么Android主线程能一直运行,并且等待着处理UI事件和其他消息。我们猜测由于主线程被某种机制阻塞,使得主线程不能退出,一直在执行状态。而Looper就提供了这种作用。 - Message
被传递的消息体 - MessageQueue
存储消息的队列 - Handler
处理消息的处理器
Looper
image.png有几个主要的变量,sThreadLocal,sMainLooper,mQueue,mThread
image.png
image.png
-
prepare()方法往sThreadLocal这个类静态变量里面放置了一个Looper对象,在set之前先判断是否有值,如果有则抛出异常。从ThreadLocal的学习我们知道,ThreadLocal的set()和get()方法会区分线程,也就是说线程和Looper对象会一一对应。
-
Looper的构造函数需要一个quitAllowed参数,在构造函数里,将该参数传给了MessageQueue,并实例化该对象赋值给mQueue变量。mThread变量表示的是,实例化Looper时,即调用Looper.prepare()方法时所在的线程。
-
同样prepareMainLooper()则是做了更多一步操作,设置
sMainLooper,myLooper()是从sThreadLocal里面取值。为什么调用myLooper()得到sMainLooper呢。因为prepareMainLooper()方法是系统调用的,系统会在应用启动时调用设置sMainLooper这个静态变量。
Looper还有两个重要的方法,loop()以及quit()。
- loop()方法会取到当前线程对应的Looper对象,然后用for()循环不断从Looper对象的mQueue里面取出Message对象,dispatchMessage这些对象。在msg为空时,跳出循环。结束loop方法。
- quit()方法则是调用mQueue的quit方法。
Message
image.png几个常用的public变量:what,arg1,arg2,obj
image.png
-
replyTo 和 sendingUid 是Messager使用时需要用到,暂时不需要关注。
-
data的作用和obj类似,区别在于obj在跨进程通信时,只能存放系统的Parcelable对象,其他类型的数据可以通过data传输。
-
target是一个Handler对象,在整个消息机制中需要使用到,后续再看。
Message对象存储的信息可以是存储在arg1,arg2,obj或是data中,也可以是一个存储在callback的Runnable对象。 -
next,指向Message对象的引用,MessageQueue中会使用到。
-
flags 消息的int型标志位,最后一位是否使用,倒数第二位是否为同步消息
image.png
Message提供了一系列签名不同的obtain方法,其他需要传参的方法都是在调用obtain()之后,对返回Message对象设置成员变量。obtain()方法会判断sPool变量是否为空,如果为空就返回一个新的Message对象。如果不为空则返回sPool指向的Message对象,将该对象使用标志flags置为未使用,将next置为null(无法通过获取到的message对象访问下一个可使用的message),sPoolSize--,并且将sPool指向message.next。
recycle()方法用来回收被使用过的Message对象,在回收前会先检查当前对象是否在使用,然后清除message所有字段,将使用标志置为被使用中,放入消息池中。
总结来说,obtain()是从message链表取出可被使用的message对象,这个链表是通过Message.next字段实现的,通过recycle()回收对象,obtain()再次使用,避免了新对象的生成。通过Message的sPool静态字段,维护这个链表。
MessageQueue
MessageQueue是一个按时间排序的消息队列。严格来说他并不是消息队列,插入消息会按照时间排序插入而不是从队尾插入,取出消息在设置同步障碍时,也不是从队头取出。
image.png
MessageQueue构造器使用了quitAllowed参数,该参数只在quit()方法里面用到。从抛出的异常信息我们知道,主线程对应的MessageQueue对象不允许调用quit方法。只有主线程对应的MessageQueue传递了false,其他线程传递的应该都是true。MessageQueue对象的创建依赖于native的实现。在创建时候获取了一个mPtr指针,在quit的时候,用该指针去释放相关的资源。
Looper里面循环调用queue.next()获取Message对象,分发Message。下面是MessageQueue的next()方法
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
...
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
1.nativePollOnce的作用是阻塞nextPollTimeoutMillis毫秒的时间。
2.在msg.target为null时,返回第一个达到when的异步消息。msg.target一般不为null,只有通过postSyncBarrier插入同步障碍Message时,target才会为null,此时不处理同步消息,只处理异步消息。
3.在msg.target不为null时,取出第一个达到when的消息。
4.在mMessages里取不到新消息时,nextPollTimeoutMillis为-1,nativePollOnce会一直阻塞。此方法造成了looper里queue.next()的阻塞。直到有新消息到来,调用nativeWake(mPtr)。
5.在调用Looper.quit()时,mQuitting会导致next里面循环退出。
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.
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) {
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;
}
-
插入消息时,会验证target和isInUse,以及mQuitting。
-
将消息标志flag置为InUse。在Message.recycle()和MessageQueue.enqueueMessage(Message msg, long when)时,都会判断该字段。
-
在链表为空,或when=0,或when小于链表里message第一个when,将新message插入头部。唤起由nativePollOnce造成的阻塞。
-
在链表不为空。(当前被阻塞,上一条是同步障碍消息,当前是异步消息)->唤起阻塞。根据when插入链表,越大的时间,插入到越后面。整条链表是按时间排序的
hasMessages(...)方法根据里面的参数,判断链表里面是否有对应消息。这个消息包括两种,正常的消息以及runnable消息。
removeMessages移除对应的某一种消息
removeCallbacksAndMessages则是移除复合条件的所有消息
removeAllMessagesLocked移除所有消息
removeAllFutureMessagesLocked移除当前时间的之后的所有消息。
Handler
handler是message.target所对应的对象。在Looper.loop()中,在通过MessageQueue.next()取到msg之后,调用msg.target.dispatchMessage(msg)。我们看看Handler的相关方法
image.png
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();
}
...
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
1.Handler的构造器参数Callback会赋值给mCallback。根据当前线程获取mLooper,以及mLooper里面的MessageQueue。如果没有Looper则会报错,这也就是常见的在子线程new Handler()会报错,因为子线程默认没有Looper。
2.在dispatchMessage时,先判断当前message是不是runnable消息。如果是,则调用message.callback.run()执行;如果不是,先尝试使用mCallback.handleMessage()处理消息,如果mCallback为空,或者mCallback.handleMessage()返回false,调用Handler.handleMessage(Message msg)方法。该方法是空方法,handler子类自行实现。
3.Handler的obtainMessage系列方法直接调用Message的obtain的对应方法。post系列方法,是将runnable对象包装成Message对象,调用MessageQueue.enqueueMessage()方法将消息放入待处理队列中;
send系列方法是将Message对象调用MessageQueue.enqueueMessage()方法放入待处理队列中。hasMessages和removeMessages方法是直接调用MessageQueue对应的方法。
4.Handler里面有个mAsynchronous成员变量,这个变量只有在调用Handler.createAsync静态方法创建新Handler时候,赋值为true,其他时候都为false。该值为true时,在enqueueMessage的时候,会将Message对象设置成异步消息,也就是说,异步的Handler发送的消息,都是异步消息。
Android主线程启动
#ActivityThread类(挑选出部分代码)
public final class ActivityThread extends ClientTransactionHandler {
final Looper mLooper = Looper.myLooper();
final H mH = new H();
final Handler getHandler() {
return mH;
}
class H extends Handler {
public static final int BIND_APPLICATION = 110;
...
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
case BIND_APPLICATION: return "BIND_APPLICATION";
...
}
}
return Integer.toString(code);
}
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
((SomeArgs) obj).recycle();
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
1.ActivityThread.main()方法是android应用主线程的入口,该方法在ZygoteInit里被调用。
2.先调用Looper.prepareMainLooper(),创建一个和主线程对应的Looper。
3.创建ActivityThread实例,启动相关的类。ActivityThread.mH 是一个继承自Handler子类对象,从handleMessage方法,看到了“serviceCreate”,“serviceBind”等方法。这个类是用来处理来自系统的一些消息事件。
4.Looper.loop()循环从MessageQueue里取出消息,处理消息,阻塞主线程。
总结
1.Looper通过ThreadLocal机制,保证该对象的实例和线程一一对应。在一个新开启的线程里,通过Looper.prepare(),创建实例。在实例化时,MessageQueue也被实例化出来,作为mQueue成员变量保存在Looper对象里。Looper,MessageQueue,Thread三者一一对应。
2.Looper.loop()开启,Looper所在线程将一直从MessageQueue里面取出Message,调用Message.target对应的Handler对象去处理消息。
3.Handler的创建默认取当前线程的Looper,也可以传入一个已有的Looper。Handler对象可以往MessageQueue里插入消息,等到了处理时间,该Handler对象便会去处理消息。Handler对象可以在任意线程post或sendMessage,最后会在Looper所在的线程处理消息。
网友评论