对Android开发有一定了解的人都知道,Android应用程序是消息驱动的。Android应用程序的消息机制是围绕消息队列来实现的,具体的,主要通过Handler、MessageQueue、Looper三个类来实现。其中Handler用来发送和处理消息;MessageQueue表示一个消息队列,负责入队和出队消息;Looper类用于创建消息循环。本文结合源码来介绍Android的消息机制包括:消息循环的创建、消息的发送和处理。
每个Android应用程序都一个主线程ActivityThread,每个主线程都有一个消息循环。先看一下ActivityThread的main方法:
- public static void main(String[] args) {
- SamplingProfilerIntegration.start();
- // CloseGuard defaults to true and can be quite spammy. We
- // disable it here, but selectively enable it later (via
- // StrictMode) on debug builds, but using DropBox, not logs.
- CloseGuard.setEnabled(false);
- Environment.initForCurrentUser();
- // Set the reporter for event logging in libcore
- EventLogger.setReporter(new EventLoggingReporter());
- Security.addProvider(new AndroidKeyStoreProvider());
- // Make sure TrustedCertificateStore looks in the right place for CA certificates
- final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
- TrustedCertificateStore.setDefaultUserDirectory(configDir);
- 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"));
- }
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
可以看到第22行调用Looper.prepareMainLooper(),再看一下prepareMainLooper具体做了哪些工作:
- public static void prepareMainLooper() {
- prepare();
- setMainLooper(myLooper());
- myLooper().mQueue.mQuitAllowed = false;
- }
可以看到在第调用prepare,然后调用setMainLooper设置主线程的Looper。不难推测,prepare中会创建Looper,先贴上代码:
- public static void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper());
- }
prepare()里面先检查是否已经创建了Looper,如果没有,则创建一个并保存到sThreadLocal中,反之,如果已经创建了一个,则会抛出异常。通过这里,我们也可以发现,prepare只能调用一次,每个线程只有一个Looper。那这里已经创建了Looper,那么第36行的Looper.loop()又是做什么的呢?继续看源码:
- public static void loop() {
- Looper me = myLooper();
- if (me == null) {
- throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
- }
- 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();
- while (true) {
- Message msg = queue.next(); // might block
- if (msg != null) {
- if (msg.target == null) {
- // No target is a magic identifier for the quit message.
- return;
- }
- long wallStart = 0;
- long threadStart = 0;
- // This must be in a local variable, in case a UI event sets the logger
- Printer logging = me.mLogging;
- if (logging != null) {
- logging.println(">>>>> Dispatching to " + msg.target + " " +
- msg.callback + ": " + msg.what);
- wallStart = SystemClock.currentTimeMicro();
- threadStart = SystemClock.currentThreadTimeMicro();
- }
- msg.target.dispatchMessage(msg);
- if (logging != null) {
- long wallTime = SystemClock.currentTimeMicro() - wallStart;
- long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
- logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
- if (logging instanceof Profiler) {
- ((Profiler) logging).profile(msg, wallStart, wallTime,
- threadStart, threadTime);
- }
- }
- // 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.recycle();
- }
- }
- }
可以看到,在loop中会进入一个死循环,每次循环的开始调用MessageQueu.next()方法从消息队列中取出一个Message,然后调用 msg.target.dispatchMessage(msg)交给message的接受者处理。这里的msg.target就是message的接受者,其实就是一个Handler。如果消息队列为空,那么MessageQueue .next()会阻塞,直到有新的消息达到。到这里,就可以明白调用Looper.loop后,线程就进入了消息循环等待message到来并交给message对应的Handler来处理消息。正常情况下,一旦进入消息循环就不会退出,除非MessageMessage.target=null,见第16行代码。
到此为止,我们就知道如果要给线程创建一个消息循环,可以按如下方式来定义:
- class LooperThread extends Thread {
- public void run() {
- Looper.prepare();
- ......
- Looper.loop();
- }
- }
上面提到在Looper中会通过msg.target.dispatchMessage(msg)将msg分派到对应的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中先检查在Message中是否定义了消息的callback,如果有,优先调用Message的callback来处理消息;反之,如果Handler的mCallBack不为null,则调用mCallBack的handleMessage来处理,当MCallBack也为null的时候,才调用Handdler的handleMessage来处理。这里可以看出,Message有三种方式来实现它的处理逻辑:
1)在Message.callback中实现,这里的callback是一个Runnable对象;
2)在Handler的内部接口CallBack中实现;
3)重写Handler的handleMessage来实现。
阅读Handler的源码,可以发现当调用handler.post(Runnable)的时候,就是通过第1种方式来实现:
- public final boolean post(Runnable r) {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
- private final Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
通常我们会通过第3种方式来定义自己的Handler,如在主线程:
- Handler mHandler=new Handler(){
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- super.handleMessage(msg);
- ......//你的处理逻辑
- }
- };
这里看一下Handler的构造函数(还有其他几种构造函数):
- public Handler() {
- 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();
- if (mLooper == null) {
- throw new RuntimeException(
- "Can't create handler inside thread that has not called Looper.prepare()");
- }
- mQueue = mLooper.mQueue;
- mCallback = null;
- }
这里可以看到,创建Handler的时候,会在保存当前线程的Looper及Looper对应的MessageQueue。这样发送消息时Handler就能将Message发送到当前线程的消息循环中。当然,我们也可以指定Looper,这样就可以给另一个线程发送消息,如在另一个线程中给主线程发送消息。这里也可以看出,定义Handler之前一定要先调用Looper.prepare()来创建一个Looper,主线程中会在ActivityThread的main方法中创建,不需要我们手动调用。但是在子线程中,一定要先调用Looper.prepare()才能创建 Handler,否则会抛出一个异常,如第78行所示。
Handler常见的用法之一就是在子线程中更新UI,我们可以通过handler.post(Runnable)让runnable在主线程中执行。其中Activity的runOnUIThread就是利用handler来实现的,view.post(Runnable)也是相似的实现。
- public final void runOnUiThread(Runnable action) {
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(action);
- } else {
- action.run();
- }
- }
当调用handler.sendMessage()发送消息时,最终会调用Handler.sendMessageAtTime,源码如下:
- public boolean sendMessageAtTime(Message msg, long uptimeMillis)
- {
- boolean sent = false;
- MessageQueue queue = mQueue;
- if (queue != null) {
- msg.target = this;
- sent = queue.enqueueMessage(msg, uptimeMillis);
- }
- else {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- }
- return sent;
- }
在这里可以看出,发送消息的时候,将Message添加到队列中,并且会将Message的接受者设置为发送Message的handler,如代码第6行所示。前面有提到,在loop中会调用MessageQueue.next()来取消息。这里先看一下消息的入队操作:
- final boolean enqueueMessage(Message msg, long when) {
- if (msg.isInUse()) {
- throw new AndroidRuntimeException(msg
- + " This message is already in use.");
- }
- if (msg.target == null && !mQuitAllowed) {
- throw new RuntimeException("Main thread not allowed to quit");
- }
- final boolean needWake;
- synchronized (this) {
- if (mQuiting) {
- RuntimeException e = new RuntimeException(
- msg.target + " sending message to a Handler on a dead thread");
- Log.w("MessageQueue", e.getMessage(), e);
- return false;
- } else if (msg.target == null) {
- mQuiting = true;
- }
- msg.when = when;
- //Log.d("MessageQueue", "Enqueing: " + msg);
- Message p = mMessages;
- if (p == null || when == 0 || when < p.when) {
- msg.next = p;
- mMessages = msg;
- needWake = mBlocked; // new head, might need to wake up
- } else {
- Message prev = null;
- while (p != null && p.when <= when) {
- prev = p;
- p = p.next;
- }
- msg.next = prev.next;
- prev.next = msg;
- needWake = false; // still waiting on head, no need to wake up
- }
- }
- if (needWake) {
- nativeWake(mPtr);
- }
- return true;
- }
看代码的第29到35行代码,这里会根据消息的处理时间Message.when来确定消息插入到队列中的具体位置,从而保证队首的消息是下一个要处理的。在这里也可以发现,主线程的消息循环是不能退出的,如第6行所示。
前面有提到MessageQueue.next()可能会阻塞。
这里一起看一下消息的出队操作:
- final Message next() {
- int pendingIdleHandlerCount = -1; // -1 only during first iteration
- int nextPollTimeoutMillis = 0;
- for (;;) {
- if (nextPollTimeoutMillis != 0) {
- Binder.flushPendingCommands();
- }
- nativePollOnce(mPtr, nextPollTimeoutMillis);
- synchronized (this) {
- // Try to retrieve the next message. Return if found.
- final long now = SystemClock.uptimeMillis();
- final Message msg = mMessages;
- if (msg != null) {
- final long when = msg.when;
- if (now >= when) {
- mBlocked = false;
- mMessages = msg.next;
- msg.next = null;
- if (false) Log.v("MessageQueue", "Returning message: " + msg);
- msg.markInUse();
- return msg;
- } else {
- nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
- }
- } else {
- nextPollTimeoutMillis = -1;
- }
- // If first time, then get the number of idlers to run.
- if (pendingIdleHandlerCount < 0) {
- 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("MessageQueue", "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;
- }
- }
在第3行,定义变量nextPollTimeoutMills,用来描述当消息队列中没有新的消息时,当前线程需要睡眠多长时间。当nextPollTimeoutMills等于-1时,表示无限制等待,直到有新的消息到达。在没有消息到达的时候会调用nativePollOnce来检查是否有新消息需要处理,nativePollOnce是一个native函数,主要是通过Linux中的poll函数来实现的。
当队列不为空时,在第17行先判断当前时间是否大于等于message的处理时间,如果是,则返回该message,反之,计算需要等待的时间,并更新nextPollTimeoutMills。
如果队列为空,则nextPollTimeoutMills的值设置为-1。在进入睡眠之前,先调用注册到消息队列中的空闲消息处理器IdleHandler对象的queueIdle函数,以便有机会在线程空闲时执行一些操作。可以通过MessageQueue的addIdleHandler和removeIdleHandler来注册和注销一个空闲消息处理器。
到此为止,已经分析完消息循环的创建,发送消息,以及消息的处理。