Android的消息机制
消息驱动是一种进程或线程的运行模式。内部、外部的各种事件都可以放到消息队列中按序处理。这种模式特别适合处理大量的交互事件。Android应用的UI线程,同样采用了消息驱动模式,所有外部来的按键消息、触屏消息、各种系统Intent、广播等都会转化为内部的消息,然后在主线程中分发处理。
消息模型
Android中与消息机制相关的类主要有:Looper、Handler、Message和MessageQueue。
Looper类
Looper对象是线程的消息循环处理器,每个线程只能有一个Looper对象。Looper内部有一个消息队列MessageQueue,线程的所有消息都存放在这个队列中。新创建一个线程时,系统并不会马上为这个线程创建一个Looper对象,需要程序自己创建。Android在启动时,为主线程(UI线程)创建了一个Looper对象。
Handler类
Handler对象是Message的接收者和处理者。用户使用Handler对象把Message添加到消息队列中;同时通过Handler的回调方法handleMessage()来对消息队列中的Message进行处理。Handler对象在构造时和某个Looper对象关联在一起。Handler和Looper是多对一的关系,反之则不行。
Message类
Message是消息的载体。Message设计成为Parcelable类的派生类,这表明Message可以通过binder跨进程发送。
理解Looper类
Looper类的主要成员变量和方法如下:
public final class Looper { /* * API Implementation Note: * * This class contains the code required to set up and manage an event loop * based on MessageQueue. APIs that affect the state of the queue should be * defined on MessageQueue or Handler rather than on Looper itself. For example, * idle handlers and sync barriers are defined on the queue whereas preparing the * thread, looping, and quitting are defined on the looper. */ private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; public static void prepare() { } private static void prepare(boolean quitAllowed) { } public static void prepareMainLooper() { } public static Looper getMainLooper() { }
每个线程只能由一个Looper类的实例对象,Looper类的实例对象必须通过prepare()创建。prepare()方法会创建一个Looper对象,并把它保存在静态变量mThreadLocal中。一个线程中多次调用prepare()方法将会抛出异常。
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); }
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
创建Looper对象的同时,会创建一个MessageQueue对象。通过myLooper()方法可以获得Looper对象。
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
静态变量sThreadLocal的类型是模板类ThreadLocal<T>,它通过将需要保存的对象和线程id关联在一起的方式实现了线程本地存储的功能,这样放入sThreadLocal对象中的Looper对象就和创建它的线程关联在一起了。
Looper类的getMainLooper()方法将返回主线程的Looper对象。Android应用启动时会创建主线程,同时会创建一个Looper对象和主线程相关联。但是创建主线程的代码在framework中,应用层不能直接取得主线程的Looper对象。因此,Android将获得主线程Looper对象放到了Looper类中。如下:
/** * Returns the application's main looper, which lives in the main thread of the application. */ public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
有了Looper类的对象后,可以调用Looper类的loop()方法来进入消息循环。loop()是一个静态的方法,它里面有一个无限的for循环,对loop()方法的调用一般在线程的run()方法中,Looper类的典型用法如下:
class myThread extends Thread { @Override public void run() { Looper.prepare(); Looper.loop(); } }
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) { 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.msg为null,表示接到了退出的请求 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);// 分发消息 } 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(); } }
loop()方法会循环从MessageQueue队列中取出消息,然后把消息分发出去。消息分发是通过Message对象中的target变量完成的,target的类型是Handler。一个Looper对象可以对应多个Handler对象,线程的Looper对象并不是纸盒一个Handler对象相关联。
Message是消息的载体,发送者把需要传递的消息放在Message对象中,Message对象创建的时候就需要指定它的处理对象。Handler主要用来处理消息,一个Handler对象可以处理多种消息。
理解Handler类
Handler主要负责消息的发送和处理。在一个线程中可以只用一个Handler对象来处理所有消息,也可以使用多个。构造一个Handler对象需要两个参数,线程的Looper对象和消息的处理函数。对于Handler对象而言,参数Looper是必须的,因为它只能给某个线程的Looper对象发送消息,如果构造方法不指定特定的Looper对象,它会使用当前线程的Looper对象。但是参数callback不是必须的,应用程序可以通过这个callback方法来实现对消息的集中处理。也可以把处理消息的callback方法直接放到消息对象中。
Handler类是消息框架的一部分,Android把消息的定义和处理完全独立出来,线程只是提供了一个消息队列和消息响应代码的运行环境。例如,Android主线程的实现是在framework中,但是我们可以使用下面的方法来构造一个带有callback方法的消息发送给主线程。
/** * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on * the Message that is returned. * @param h Handler to assign to the returned Message object's <em>target</em> member. * @param callback Runnable that will execute when the message is handled. * @return A Message object from the global pool. */ public static Message obtain(Handler h, Runnable callback) { Message m = obtain(); m.target = h; m.callback = callback; return m; }
这样,这个callback方法将在主线程中执行。
Handler类的消息发送接口分成两类:“send”和“post”。
“send”类的接口
/** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } /** * Sends a Message containing only the what value. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } /** * Sends a Message containing only the what value, to be delivered * after the specified amount of time elapses. * @see #sendMessageDelayed(android.os.Message, long) * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } /** * Sends a Message containing only the what value, to be delivered * at a specific time. * @see #sendMessageAtTime(android.os.Message, long) * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageAtTime(msg, uptimeMillis); } /** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } /** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ 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); } /** * Enqueue a message at the front of the message queue, to be processed on * the next iteration of the message loop. You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * <b>This method is only for use in very special circumstances -- it * can easily starve the message queue, cause ordering problems, or have * other unexpected side-effects.</b> * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendMessageAtFrontOfQueue(Message msg) { 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, 0); }
所谓的“(send)发送”消息只是把消息插入到了消息队列中,同时指定消息处理的时间。如果指定的时间为0,表示要立即处理,MessageQueue会把这条消息插入到队列的头部。MessageQueue中接收消息的接口如下:
boolean enqueueMessage(Message msg, long when)
enqueueMessage()除了消息参数外,只有一个时间参数,因此,Handler类里面发送消息的接口虽多,但都是在时间上玩花样,让应用方便使用而已。
- 如果希望马上处理,但是不打算插队,使用sendMessage();
- 如果非常紧急,希望尽快处理,使用sendMessageAtFrontOfQueue();
- 如果希望延时一段时间处理,使用sendMessageDelayed();
- 如果希望在指定时间处理,使用sendMessageAtTime();
如果定义的消息只有消息id,不用附加参数,使用sendEmptyMessage()方法将会更加方便。
“post”类型的方法
/** * Causes the Runnable r to be added to the message queue. * The runnable will be run on the thread to which this handler is * attached. * * @param r The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } /** * Causes the Runnable r to be added to the message queue, to be run * at a specific time given by <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * The runnable will be run on the thread to which this handler is attached. * * @param r The Runnable that will be executed. * @param uptimeMillis The absolute time at which the callback should run, * using the {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean postAtTime(Runnable r, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r), uptimeMillis); } /** * Causes the Runnable r to be added to the message queue, to be run * at a specific time given by <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * The runnable will be run on the thread to which this handler is attached. * * @param r The Runnable that will be executed. * @param uptimeMillis The absolute time at which the callback should run, * using the {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. * * @see android.os.SystemClock#uptimeMillis */ public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); } /** * Causes the Runnable r to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the thread to which this handler * is attached. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * * @param r The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } /** * Posts a message to an object that implements Runnable. * Causes the Runnable r to executed on the next iteration through the * message queue. The runnable will be run on the thread to which this * handler is attached. * <b>This method is only for use in very special circumstances -- it * can easily starve the message queue, cause ordering problems, or have * other unexpected side-effects.</b> * * @param r The Runnable that will be executed. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean postAtFrontOfQueue(Runnable r) { return sendMessageAtFrontOfQueue(getPostMessage(r)); }
从代码的实现上看,这些“post”方法也是在使用“send”类的方法在发送消息,只是他们的参数要求是Runnable类的对象,然后在方法中调用getPostMessage()获取了一个Message对象来发送。
可以看出,“post”类型的方法用来发送带有处理方法的消息;“send”类型的方法用于发送传统的带有消息id的消息。
最后,看下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); } }
从代码中可以看出,消息会优先分发给消息中自带的回调方法;否则,如果Handler定义了回调方法,先调用这个方法处理;否则,调用Handler自己的handleMessage()方法,这个方法缺省不做任何事情。如果实现了一个Handler类的继承类,也可以通过重载这个handleMessage()方法处理消息。
消息的同步--Message类的setAsynchronous()方法
在Android的Message类中,有一个setAsynchronous(bool async)方法,表示给消息加上异步标志。
那么这个方法的具体作用是什么呢?在MessageQueue类中有一个方法enqueueSyncBarrier(long when),调用这个方法会在消息队列中插入一条没有Handler对象的消息,这条不带Handler对象的消息称为“SyncBarrier”,MessageQueue将暂停消息队列中“SyncBarrier”以后的消息。这好比一群人在排队买票,有人过来在队列中放了一块牌子:“从这开始,暂停售票”。但是这时如果还有消息需要处理,可用使用setAsynchronous()方法来给一条消息做上标志,MessageQueue检测到消息中的标志后,会正常处理这条消息,但是别的消息还是暂停处理,直到调用removeSyncBarrier()方法移走了挡在消息队列前面的“SyncBarrier”。
分析MessageQueue类
研究MessageQueue时,如果带着问题去分析,则更容易理解透彻,问题如下:
- 不同线程间发送消息,有同步保护吗?如何实现的?
- 消息队列不同于普通队列,每条消息都有时间,如何实现按时间分发消息?
- 没有消息时消息队列会挂起吗?来了新的消息又是如何唤醒的。
- 消息队列是如何组织的?新消息是如何插入的,都在队尾吗?
- “SyncBarrier”是如何工作的?
下面我们就从对象构造、消息处理、发送消息3个方面来分析MessageQueue。
MessageQueue的构造
我们先看下MessageQueue的构造方法,如下:
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
private native static long nativeInit();
从代码中看出,MessageQueue对象的构造主要是调用本地方法nativeInit()完成的,对应的是andriod_os_MessageQueue_nativeInit(),如下:
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); return reinterpret_cast<jlong>(nativeMessageQueue); }
该函数最主要的功能是新创建了一个本地的NativeMessageQueue对象。NativeMessageQueue构造函数如下:
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } }
NativeMessageQueue的构造函数只是创建一个本地的Looper类对象,从NativeMessageQueue类的代码看,它本质上是一个代理类。它把JAVA层的调用转变为对native层对Looper类的函数的调用,native层的Looper类才是关键。
native层的Looper类也实现了一套完整的消息处理机制。但是java层的Looper类和native层的Looper类并没有直接的关系。MessageQueue虽然使用了native层的Looper类,但也只使用了它的等待/唤醒机制,其余的如消息队列的实现还是在java层。因此,如果再看到MessageQueue中有从java层到native层之间的调用,可以忽略中间过程,直接分析native层的Looper类中的函数。
MessageQueue中的消息处理过程
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(); } //调用本地方法 等待nextPollTimeoutMillis毫秒,-1表示永远阻塞,0表示立即返回 nativePollOnce(ptr, nextPollTimeoutMillis); //这里使用了针对this对象同步,因此只要next方法还没有退出,再调用本对象的任何方法都将导致调用线程挂起 synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages;//mMessages指向队列头 if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. // "SyncBarrier"的标志就是其消息的target为null,忽略普通消息,查找第一条“异步消息” 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) {// 如果退出标志设置了,则销毁native对象,然后返回 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. // 第一次进入idle会检查是否安装了idle handler if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size();//获取IdleHandler的个数 } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue;// 没有安装idle handler,则继续for循环 } // idle handler放入数组mPendingIdleHandlers中 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. // 处理所有idle handler,如果回调结果为false,表示不再继续处理,则从idle handler的列表中移除该handler for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler,释放引用的IdleHandler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler);//如果queueIdle()返回false,将对应的IdleHandler从mIdleHandlers中移除 } } } // 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. // 如果有idle handler存在,把nextPollTimeoutMillis设为0,让循环继续,而不是阻塞。 nextPollTimeoutMillis = 0; } }
下面总结next()方法的功能。
- 检查队列中的第一条消息是否是“SyncBarrier”消息,如果是,寻找队列中的第一条“异步消息”,找到后设为当前处理的消息;如果不是“SyncBarrier”消息,把第一条消息设为当前处理的消息。
- 如果当前消息不为null,检查该消息的处理时间是否已经超时,如果没有,计算等待时间。如果处理时间到了,next()方法将返回该消息,并退出。
- 如果当前消息为null,表示队列中没有可以处理的消息了,设置等待时间为-1(表示永久等待)。
- 检查队列中的退出标志,如果检测到退出标志则销毁native层中创建的对象,然后next()方法退出。
next()里另一个要说的是那些Idle Handler,当消息队列中没有消息需要马上处理时,会判断用户是否设置了Idle Handler,如果有的话,则会尝试处理mIdleHandlers中所记录的所有Idle Handler,此时会逐个调用这些Idle Handler的queueIdle()成员函数。
- 检查是否已经安装了处于idle状态的回调函数。如果没有安装,回到循环的开始,调用nativePollOnce()方法挂起线程并等待新消息的到来。
- 如果安装了idle状态的回调函数则调用所有回调函数,同时把epoll的等待时间设为0,这表明在安装了idle处理函数的情况下消息队列的循环处理是不会阻塞的,这样idle处理函数将会不停地被调用,直到处理函数的值为false。
向MessageQueue发消息
向MessageQueue发消息使用的是enqueueMessage()方法,如下:
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) {//如果消息的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) {//使用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;//p指向消息队列头 boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. // 队列中没有消息,或者消息需要插入到队列头(when=0表示需要立即处理;when<p.when表示当前消息比mMessages靠前处理) 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. // 如果设置了“SyncBarrier”,只有插入了“异步消息”才需要唤醒 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; }
enqueueMessage()方法插入消息时根据时间来排序,时间早的插在前面。
消息队列的组织也非常简单,利用了Message类的next指针形成一个从头指向尾的单向链表。插入时计算是否需要唤醒处理线程。enqueueMessage()方法会尽量地避免唤醒处理线程,只有插入了一条马上要处理的消息,或者在暂停处理消息的情况下,又插入了“异步消息”的情况下才会去唤醒处理线程。
为什么其余情况下插入消息不需要唤醒处理线程呢?其余的情况都是把消息放到队列的中部或尾部(时间未到)。如果前面还有消息没处理,这条消息就更不着急去处理了。
从代码中可以了解到,通过安装idle handler可以得到消息队列进入idle的通知。因此,一旦消息队列为空,应用就可以运行一些优先级较低但是耗时的代码。这些代码将在不影响程序消息处理的情况下,利用空闲时间来运行。
进程间的消息传递
Android的消息可以在进程之间传递。进程间消息传递是建立在Binder通信基础之上的。Binder本身用来在进程间传递信息已经足够了,这里介绍的进程间消息传递方法只是让应用在设计上更加便利,并不是架构上大的改进。
我们知道,只要有了Binder的引用对象就可以调用其功能,Android中如果希望向另一个进程的Handler发送消息,一定要通过某个Binder对象来代理完成。在Handler类中getIMessenger()会创建一个Binder对象,代码如下:
final IMessenger getIMessenger() { synchronized (mQueue) { if (mMessenger != null) { return mMessenger; } mMessenger = new MessengerImpl(); return mMessenger; } }
getIMessenger()方法中创建的对象类型是MessengerImpl,它是一个Binder的服务类,从IMessenger.Stub类派生。MessengerImpl中的send()方法就是给Handler发送消息。如下:
private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) { msg.sendingUid = Binder.getCallingUid(); Handler.this.sendMessage(msg); } }
因此,调用某个Handler对象的getIMessenger()方法将得到能给这个Handler对象发送消息的Binder对象。但是要跨进程发送消息,还需要把这个Binder对象传递到调用进程中。
理解Messenger类
Messenger类实际上是对IMessenger对象的包装,它里面包含了一个Handler对象关联的MessengerImpl对象的引用。Messenger类的构造方法如下:
/** * Create a new Messenger pointing to the given Handler. Any Message * objects sent through this Messenger will appear in the Handler as if * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had * been called directly. * * @param target The Handler that will receive sent messages. */ public Messenger(Handler target) { mTarget = target.getIMessenger();//获得MessengerImpl对象的引用 }
使用Messenger类的好处就是隐藏了通信中使用Binder的细节,让整个过程看起来就像是在发送一个本地消息一样简单。Messenger类的send()方法用来发送消息,它也是通过Binder对象来发送消息。如下:
/** * Send a Message to this Messenger's Handler. * * @param message The Message to send. Usually retrieved through * {@link Message#obtain() Message.obtain()}. * * @throws RemoteException Throws DeadObjectException if the target * Handler no longer exists. */ public void send(Message message) throws RemoteException { mTarget.send(message); }
send()方法并不是在本地调用的,通常要把Messenger对象传递到另一个进程后再使用。Messenger类实现了Parcelable接口,可以很方便地传递到另一个进程。
如果只需要在两个进程间进行简单的消息往来,上面使用Messenger就够了。为了方便应用使用,Android还提供了AsyncChannel类来完成建立双向通信的过程。
建立通信通道--AsyncChannel类的作用
AsyncChannel用来建立两个Handler之间的通信通道。这两个Handler可以在一个进程中也可以在两个进程中。但是通信双方的地位并不一定是对等的,一方要充当server并响应AsyncChannel中定义的消息,另一方则充当client的角色,主动去连接对方。
使用AsyncChannel类首先要确定通信双方使用“半连接模式”还是“全连接模式”。“半连接模式”是指建立连接后,只能客户端主动给服务端发送消息,服务端可以在收到客户端的消息后利用消息中附带的Messenger对象来给客户端“回复”消息,但不能主动给客户端发送消息。“全连接模式”则是双方都能主动给对方发生消息。显然,“全连接模式”比“半连接模式”占用更多的系统资源。
半连接模式
AsyncChannel类中提供改了很多个连接方式,客户端需要根据情况使用。但是在连接前,还是要先得到服务端的Messenger对象。java层中Binder的传递有两种方式,一是通过建立好的Binder通道来传递Binder对象,二是通过组件Service来获得Service中包含的Binder对象。AsyncChannel同时支持这两种方式。
如果客户端和服务已经建立了Binder通道,那么服务的Messenger对象就可以通过它传递到客户端,这样我们就可以使用AsyncChannel类的connect()方法来建立两者之间的联系了。
/** * Connect handler and messenger. * * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. * msg.arg1 = status * msg.obj = the AsyncChannel * * @param srcContext * @param srcHandler * @param dstMessenger */ public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) { if (DBG) log("connect srcHandler to the dstMessenger E"); // We are connected connected(srcContext, srcHandler, dstMessenger); // Tell source we are half connected replyHalfConnected(STATUS_SUCCESSFUL); if (DBG) log("connect srcHandler to the dstMessenger X"); }
connect()方法的参数srcContext是客户端的上下文,srcHandler是客户端的Handler对象,dstMessenger是从服务端传递来的Messenger对象。
AsyncChannel还为通信双方定义了非常简单的握手协议,connect()方法会调用replyHalfConnected()方法来发送CMD_CHANNEL_HALF_CONNECTED消息给客户端的srcHandler。为什么消息是发给客户端中的Handler对象而不是发到服务端呢?其实这个简单的握手协议主要是为了全连接模式准备的,半连接模式只要得到了对方的Messenger就可以通信了,但是全连接模式下必须先确保通信双方都准备好了才能开始通信过程,因此需要一个简单的握手协议。在半连接情况下的srcHandler对象不需要去处理CMS_CHANNEL_HALF_CONNECTED消息,调用完connect()方法后就可以开始发送消息了。
如果客户端和服务端之间没有现成的Binder通道,可以通过组件Service的方式来建立一个Binder通道,当然,在服务端实现的Service中包含Binder对象必须是MessengerImpl对象。如果不愿意重新写一个service,Android中提供了一个类AsyncService,服务端可以直接使用它来创建一个Service。这种情况下,客户端需要使用AsyncChannel类的另一个connect()方法来建立连接。
/** * Connect handler to named package/class. * * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete. * msg.arg1 = status * msg.obj = the AsyncChannel * * @param srcContext is the context of the source * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED * messages * @param dstPackageName is the destination package name * @param dstClassName is the fully qualified class name (i.e. contains * package name) */ public void connect(Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) { if (DBG) log("connect srcHandler to dst Package & class E"); final class ConnectAsync implements Runnable { Context mSrcCtx; Handler mSrcHdlr; String mDstPackageName; String mDstClassName; ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) { mSrcCtx = srcContext; mSrcHdlr = srcHandler; mDstPackageName = dstPackageName; mDstClassName = dstClassName; } @Override public void run() { int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName); replyHalfConnected(result); } } ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName); new Thread(ca).start(); if (DBG) log("connect srcHandler to dst Package & class X"); }
connect()方法会根据传入的包名和类名去启动Service,启动的时间可能比较长,因此,connect()方法中使用了线程去启动Service。方法中调用了connectSrcHandlerToPackageSync()方法,它会调用bindService()方法来启动一个Service组件,代码如下:
/** * Connect handler to named package/class synchronously. * * @param srcContext is the context of the source * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED * messages * @param dstPackageName is the destination package name * @param dstClassName is the fully qualified class name (i.e. contains * package name) * * @return STATUS_SUCCESSFUL on success any other value is an error. */ public int connectSrcHandlerToPackageSync( Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName) { if (DBG) log("connect srcHandler to dst Package & class E"); mConnection = new AsyncChannelConnection(); /* Initialize the source information */ mSrcContext = srcContext; mSrcHandler = srcHandler; mSrcMessenger = new Messenger(srcHandler); /* * Initialize destination information to null they will * be initialized when the AsyncChannelConnection#onServiceConnected * is called */ mDstMessenger = null; /* Send intent to create the connection */ Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClassName(dstPackageName, dstClassName); boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); if (DBG) log("connect srcHandler to dst Package & class X result=" + result); return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL; }
Service启动后,还必须实现ServiceConnection类来接收Service传递回来的Binder对象。AsyncChannel中的定义如下:
/** * ServiceConnection to receive call backs. */ class AsyncChannelConnection implements ServiceConnection { AsyncChannelConnection() { } @Override public void onServiceConnected(ComponentName className, IBinder service) { mDstMessenger = new Messenger(service); replyHalfConnected(STATUS_SUCCESSFUL); } @Override public void onServiceDisconnected(ComponentName className) { replyDisconnected(STATUS_SUCCESSFUL); } }
在onServiceConnected()方法中把传递回来的Binder引用对象包装成了Messenger对象并保存在mDstMessenger变量中,如果需要给服务端发送消息,调用mDstMessenger对象的send()方法就可以了。这里也调用了replyHalfConnected()方法,给客户端的Handler发送CMD_CHANNEL_HALF_CONNECTED消息,在这种情形下就有意义了,客户端的Handler对象收到消息才可以和服务端通信。
全连接模式
全连接模式是建立在半连接模式基础上的,当客户端的Handler对象收到消息CMD_CHANNEL_HALF_CONNECTED以后,如果希望建立全连接,需要再向服务端发送CMD_CHANNEL_FULL_CONNECTION消息,同事在消息中附上客户端的Messenger对象。服务端收到消息后,还需要给客户端回复CMD_CHANNEL_FULLY_CONNECTED,如果服务端同意建立全连接,会将消息的第一个参数msg.arg1的值设置为0,否则设置为非0。
以WifiService和客户端WifiManager为例,进行说明全连接模式的建立过程。先看客户端Handler的实现:
private class ServiceHandler extends Handler { ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message message) { synchronized (sServiceHandlerDispatchLock) { dispatchMessageToListeners(message); } } private void dispatchMessageToListeners(Message message) { Object listener = removeListener(message.arg2); switch (message.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); } else { Log.e(TAG, "Failed to set up channel connection"); // This will cause all further async API calls on the WifiManager // to fail and throw an exception mAsyncChannel = null; } mConnected.countDown(); break; case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: // Ignore break; ......
WifiManager收到了消息CMS_CHANNEL_HALF_CONNECTED后,调用sendMessage()方法给WifiService发送了一条CMD_CHANNEL_FULL_CONNECTION的消息,sendMessage()方法会把本端的Messenger对象附加到信息中。
服务端是如何处理接收到的消息的,WifiService中Handler定义如下:
/** * Handles client connections */ private class ClientHandler extends Handler { ClientHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); // We track the clients by the Messenger // since it is expected to be always available mTrafficPoller.addClient(msg.replyTo); } else { Slog.e(TAG, "Client connection failure, error=" + msg.arg1); } break; } case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { if (DBG) Slog.d(TAG, "Send failed, client connection lost"); } else { if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); } mTrafficPoller.removeClient(msg.replyTo); break; } case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { AsyncChannel ac = new AsyncChannel(); ac.connect(mContext, this, msg.replyTo); break; } ...... } } private void replyFailed(Message msg, int what, int why) { Message reply = msg.obtain(); reply.what = what; reply.arg1 = why; try { msg.replyTo.send(reply); } catch (RemoteException e) { // There's not much we can do if reply can't be sent! } } } private ClientHandler mClientHandler;
服务端收到CMD_CHANNEL_FULL_CONNECTION消息后,创建自己的AsyncChannel对象,然后调用它的connect()方法和客户端的Messenger进行绑定。其实,AsyncChannel对象也只能向一个方向发送消息,所谓的双向通信就是双方都有自己的AsyncChannel来发送消息。前面介绍了,调用connect()方法会给本端的Handler对象发送CMD_CHANNEL_HALF_CONNECTED消息,因此,服务端也会对这条消息进程处理,处理的方式是把客户端的Messenger对象保存到成员变量mTrafficPoller数组中。对服务端而言,它连接的客户端可能是多个,因此,他要用数组保存所有客户端的Messenger。从上面的分析不难看出,AsyncChannel的作用就是实现了建立双向Binder连接的过程,方便应用使用。不过AsyncChannel也提供了新的功能:同步消息发送。
同步消息发送
AsyncChannel的sendMessageSynchronously()方法可以用来发送同步消息。sendMessageSynchronously()方法发送完消息后会挂起线程进入等待状态,收到回复的消息后再恢复线程的运行。AsyncChannel类中定义了一个潜入类SyncMessenger来完成同步消息的发送,代码如下:
/** * Send a message synchronously. * * @param msg to send * @return result message or null if an error occurs */ private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) { SyncMessenger sm = SyncMessenger.obtain(); try { if (dstMessenger != null && msg != null) { msg.replyTo = sm.mMessenger; synchronized (sm.mHandler.mLockObject) { dstMessenger.send(msg); sm.mHandler.mLockObject.wait(); } } else { sm.mHandler.mResultMsg = null; } } catch (InterruptedException e) { sm.mHandler.mResultMsg = null; } catch (RemoteException e) { sm.mHandler.mResultMsg = null; } Message resultMsg = sm.mHandler.mResultMsg; sm.recycle(); return resultMsg; } }
在发往另一端的消息中,msg.replyTo设置成SyncMessenger对象,这样,回复消息将不会送到AsyncChannel的srcHandler对象,而是被SyncMessenger对象收到。同时,发送完消息后,调用wait()方法挂起线程。服务端发送的回复消息将在SyncMessenger中定义的SyncHandler中处理。如下:
/** Synchronous Handler class */ private class SyncHandler extends Handler { /** The object used to wait/notify */ private Object mLockObject = new Object(); /** The resulting message */ private Message mResultMsg; /** Constructor */ private SyncHandler(Looper looper) { super(looper); } /** Handle of the reply message */ @Override public void handleMessage(Message msg) { mResultMsg = Message.obtain();//保存回复消息 mResultMsg.copyFrom(msg); synchronized(mLockObject) { mLockObject.notify(); } } }
上面的代码中把回复消息保存到mResultMsg中,之后调用notify()方法解锁线程。线程恢复运行后将返回收到的消息,这样一次同步消息的发送就结束了。