android的消息队列机制

android下的线程,Looper线程,MessageQueue,Handler,Message等之间的关系,以及Message的send/post及Message dispatch的过程。

Looper线程

我们知道,线程是进程中某个单一顺序的控制流,它是内核做CPU调度的单位。那何为Looper线程呢?所谓Looper线程,即是借助于Looper和MessageQueue来管理控制流的一类线程。在android系统中,app的主线程即是借助于Looper和MessageQueue来管理控制流的,因而主线就是一个特殊的Looper线程。其实,不仅仅只有主线程可以用Looper和MessageQueue来管理控制流,其它的线程也一样可以。我们可以先看一下android源代码(Looper类,位置为frameworks/base/core/java/android/os/Looper.java)的注释中给出的一种Looper线程的实现方式:

package com.example.messagequeuedemo;import android.os.Handler;
import android.os.Looper;
import android.util.Log;public class LooperThread extends Thread {public static final String TAG = MainActivity.TAG;private static final String CompTAG = "LooperThread";public Handler mHandler;@Overridepublic void run() {Log.d(TAG, CompTAG + ": LooperThread=>run");Looper.prepare();mHandler = new Handler() {public void handleMessage(android.os.Message msg) {Log.d(TAG, CompTAG + ": LooperThread=>Handler=>handleMessage");// process incoming message here}};Looper.loop();}
}

可以看到,就是在线程的run()方法中,调用Looper.prepare()做一些初始化,然后创建一个Handler对象,最后执行Looper.loop()启动整个的事件循环。就是这么简单的几行代码,一个可以使用消息队列来管理线程执行流程的Looper 线程就创建好了。

那么上面的每一行代码,具体又都做了些什么呢?接着我们就来看下,神秘的Looper.prepare()到底都干了些什么:

     // sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();/** 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);mRun = true;mThread = Thread.currentThread();}

由这段代码可知,Looper.prepare()是一个静态方法,它做的事情就是简单地为当前的线程创建一个Looper对象,并存储在一个静态的线程局部存储变量中。在Looper的构造函数中又创建了一个MessageQueue对象。同时Looper会引用到当前的线程,并将一个表示执行状态的变量mRun设置为true。对于此处的线程局部存储变量sThreadLocal,可以简单地理解为一个HashMap,该HashMap中存放的数据其类型为Looper,key则为Thread,而每个线程又都只能获得特定于这个线程的key,从而访问到专属于这个线程的数据。

启动Looper线程就和启动普通的线程一样,比如:

public class MainActivity extends Activity {public static final String TAG = "MessageQueueDemo";private static final String CompTAG = "MainActivity";private LooperThread mLooperThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {Log.d(TAG, CompTAG + ": MainActivity=>onCreate");super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mLooperThread = new LooperThread();mLooperThread.start();}

同样是new一个对象,然后调用该对象的start()方法。

Android SDK本身也提供了一个class来实现LooperThread那样的机制,以方便在app中创建一个执行事件处理循环的后台线程,但提供的功能要完善得多,这个class就是HandlerThread。我们可以看一下这个class的定义:

package android.os;
/*** Handy class for starting a new thread that has a looper. The looper can then be * used to create handler classes. Note that start() must still be called.*/
public class HandlerThread extends Thread {int mPriority;int mTid = -1;Looper mLooper;public HandlerThread(String name) {super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;}/*** Constructs a HandlerThread.* @param name* @param priority The priority to run the thread at. The value supplied must be from * {@link android.os.Process} and not from java.lang.Thread.*/public HandlerThread(String name, int priority) {super(name);mPriority = priority;}/*** Call back method that can be explicitly overridden if needed to execute some* setup before Looper loops.*/protected void onLooperPrepared() {}@Overridepublic void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}/*** This method returns the Looper associated with this thread. If this thread not been started* or for any reason is isAlive() returns false, this method will return null. If this thread * has been started, this method will block until the looper has been initialized.  * @return The looper.*/public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}/*** Quits the handler thread's looper.* <p>* Causes the handler thread's looper to terminate without processing any* more messages in the message queue.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p><p class="note">* Using this method may be unsafe because some messages may not be delivered* before the looper terminates.  Consider using {@link #quitSafely} instead to ensure* that all pending work is completed in an orderly manner.* </p>** @return True if the looper looper has been asked to quit or false if the* thread had not yet started running.** @see #quitSafely*/public boolean quit() {Looper looper = getLooper();if (looper != null) {looper.quit();return true;}return false;}/*** Quits the handler thread's looper safely.* <p>* Causes the handler thread's looper to terminate as soon as all remaining messages* in the message queue that are already due to be delivered have been handled.* Pending delayed messages with due times in the future will not be delivered.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p><p>* If the thread has not been started or has finished (that is if* {@link #getLooper} returns null), then false is returned.* Otherwise the looper is asked to quit and true is returned.* </p>** @return True if the looper looper has been asked to quit or false if the* thread had not yet started running.*/public boolean quitSafely() {Looper looper = getLooper();if (looper != null) {looper.quitSafely();return true;}return false;}/*** Returns the identifier of this thread. See Process.myTid().*/public int getThreadId() {return mTid;}
}

它不仅有事件处理循环,还给app提供了灵活地设置HandlerThread的线程优先级的方法;事件循环实际启动前的回调(onLooperPrepared());获取HandlerThreadLooper的方法(这个方法对于Handler比较重要);以及多种停掉HandlerThread的方法——quit()quitSafely()——以避免资源的leak。

Looper线程有两种,一种是我们上面看到的那种由app自己创建,并在后台运行的类型;另外一种则是android Java应用的主线程。创建前者的Looper对象需要使用Looper.prepare()方法,而创建后者的,则需使用Looper.prepareMainLooper()方法。我们可以看一下Looper.prepareMainLooper()的实现:

    /*** Initialize the current thread as a looper, marking it as an* application's main looper. The main looper for your application* is created by the Android environment, so you should never need* to call this function yourself.  See also: {@link #prepare()}*/public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}

比较特别的地方即在于,此处调用prepare()方法传进去的quitAllowed参数为false,即表示这个Looper不能够被quit掉。其他倒是基本一样。整个android系统中,调用到prepareMainLooper()方法的地方有两个:

/frameworks/base/services/java/com/android/server/
H A D	SystemServer.java	94 Looper.prepareMainLooper();
/frameworks/base/core/java/android/app/
H A D	ActivityThread.java	5087 Looper.prepareMainLooper();

一处在SystemServer——system_server的主线程——的run()方法中,用于为system_server主线程初始化消息处理队列;另外一处在ActivityThread的run()方法中,自然即是创建android app主线程的消息处理队列了。

通过消息与Looper线程交互

Looper线程的特别之处究竟在哪里呢?如前所述,这种线程有一个Looper与之关联,会使用消息队列,或者称为事件循环来管理执行的流程。那这种特别之处又如何体现呢?答案即是其它线程可以向此类线程中丢消息进来(当然此类线程本身也可以往自己的消息队列里面丢消息),然后在事件处理循环中,这些事件会得到处理。那究竟要如何往Looper线程的消息队列中发送消息呢?

回忆前面我们创建Looper线程的那段代码,不是有创建一个Handler出来嘛。没错,就是通过Handler来向Looper线程的MessageQueue中发送消息的。可以看一下使用HandlerLooper线程发送消息的方法。LooperThread的代码与上面的一样,Looper线程发送消息的部分的写法:

package com.intel.helloworld;import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;public class MainActivity extends Activity {public static final String TAG = "LifecycleDemoApp";private static final String CompTAG = "MainActivity";public static final int MESSAGE_WHAT_CREATE = 1;public static final int MESSAGE_WHAT_START = 2;public static final int MESSAGE_WHAT_RESUME = 3;public static final int MESSAGE_WHAT_PAUSE = 4;public static final int MESSAGE_WHAT_STOP = 5;public static final int MESSAGE_WHAT_DESTROY = 6;LooperThread mThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {Log.d(TAG, CompTAG + ": " + "Activity-->onCreate");super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mThread = new LooperThread();mThread.start();}@Overrideprotected void onStart() {Log.d(TAG, CompTAG + ": " + "Activity-->onStart");super.onStart();Handler handler = mThread.mHandler;Message msg = Message.obtain();msg.what = MESSAGE_WHAT_START;handler.sendMessage(msg);}@Overrideprotected void onResume() {Log.d(TAG, CompTAG + ": " + "Activity-->onResume");super.onResume();Handler handler = mThread.mHandler;Message msg = Message.obtain();msg.what = MESSAGE_WHAT_RESUME;handler.sendMessage(msg);}@Overrideprotected void onPause() {Log.d(TAG, CompTAG + ": " + "Activity-->onPause");super.onPause();Handler handler = mThread.mHandler;Message msg = Message.obtain();msg.what = MESSAGE_WHAT_PAUSE;handler.sendMessage(msg);}@Overrideprotected void onStop() {Log.d(TAG, CompTAG + ": " + "Activity-->onStop");super.onStop();Handler handler = mThread.mHandler;Message msg = Message.obtain();msg.what = MESSAGE_WHAT_STOP;handler.sendMessage(msg);}@Overrideprotected void onDestroy() {Log.d(TAG, CompTAG + ": " + "Activity-->onDestroy");super.onDestroy();Handler handler = mThread.mHandler;Message msg = Message.obtain();msg.what = MESSAGE_WHAT_DESTROY;handler.sendMessage(msg);}@Override public boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overrideprotected void onSaveInstanceState(Bundle outState) {Log.d(TAG, CompTAG + ": " + "Activity-->onSaveInstanceState");super.onSaveInstanceState(outState);}
}

使用Handler向一个Looper线程发送消息的过程,基本上即是,调用Message.obtain()Handler.obtainMessage()获取一个Message对象->设置Message对象->调用在Looper线程中创建的Handler对象的方法发送消息。

Handler究竟是如何知道要向哪个MessageQueue发送消息的呢?从前面的代码中,我们似乎看不到任何HandlerMessageQueue能关联起来的迹象。这究竟是怎么回事呢?这也是我们特别强调要使用Looper线程中创建的Handler对象来向该Looper线程中发送消息的原因。我们可以看一下Handler对象构造的过程:

    /*** Default constructor associates this handler with the {@link Looper} for the* current thread.** If this thread does not have a looper, this handler won't be able to receive messages* so an exception is thrown.*/public Handler() {this(null, false);}/*** Constructor associates this handler with the {@link Looper} for the* current thread and takes a callback interface in which you can handle* messages.** If this thread does not have a looper, this handler won't be able to receive messages* so an exception is thrown.** @param callback The callback interface in which to handle messages, or null.*/public Handler(Callback callback) {this(callback, false);}/*** Use the provided {@link Looper} instead of the default one.** @param looper The looper, must not be null.*/public Handler(Looper looper) {this(looper, null, false);}/*** Use the provided {@link Looper} instead of the default one and take a callback* interface in which to handle messages.** @param looper The looper, must not be null.* @param callback The callback interface in which to handle messages, or null.*/public Handler(Looper looper, Callback callback) {this(looper, callback, false);}/*** Use the {@link Looper} for the current thread* and set whether the handler should be asynchronous.** Handlers are synchronous by default unless this constructor is used to make* one that is strictly asynchronous.** Asynchronous messages represent interrupts or events that do not require global ordering* with represent to synchronous messages.  Asynchronous messages are not subject to* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier long)}.** @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.** @hide*/public Handler(boolean async) {this(null, async);}/*** Use the {@link Looper} for the current thread with the specified callback interface* and set whether the handler should be asynchronous.** Handlers are synchronous by default unless this constructor is used to make* one that is strictly asynchronous.** Asynchronous messages represent interrupts or events that do not require global ordering* with represent to synchronous messages.  Asynchronous messages are not subject to* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier long)}.** @param callback The callback interface in which to handle messages, or null.* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.** @hide*/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();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;}/*** Use the provided {@link Looper} instead of the default one and take a callback* interface in which to handle messages.  Also set whether the handler* should be asynchronous.** Handlers are synchronous by default unless this constructor is used to make* one that is strictly asynchronous.** Asynchronous messages represent interrupts or events that do not require global ordering* with respect to synchronous messages.  Asynchronous messages are not subject to* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.** @param looper The looper, must not be null.* @param callback The callback interface in which to handle messages, or null.* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.** @hide*/public Handler(Looper looper, Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}

Handler的构造函数共有7个,其中4个不需要传递Looper参数,3个需要。前面我们用的是不需要传递Looper对象的构造函数,因而现在我们主要关注那4个不需要传递Looper参数的。它们都是Handler(Callback callback, boolean async)不同形式的封装,而在这个构造函数中是通过Looper.myLooper()获取到当前线程的Looper对象,并与相关的MessageQueue关联起来的。这也是前面我们在实现Looper线程时,要在其run方法中创建一个public Handler的依据。

当然我们也可以使用那些能够手动传递Looper对象的构造函数,在构造Handler对象时,显式地使其与特定的Looper/消息队列关联起来。比如配合HandlerThread.getLooper()方法来用。

(这个地方我们看到,创建Handler对象时,可以传递另外两个参数,一个是Callback,另一个是async,那这两个参数在这套消息队列机制中,又起到一个什么样的作用呢?后面我们在来解答这个问题。)

Handler提供了两组函数用于向一个Looper线程的MessageQueue中发送消息,分别是postXXX()族和sendXXX()族,这两组函数的调用关系大体如下所示:

085042_SlZe_919237.jpg

我们可以先看一下sendXXX()族消息发送方法:

    /*** 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>* 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);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

这些方法之间大多只有一些细微的差别,它们最终都调用Handler.enqueueMessage()/MessageQueue.enqueueMessage()方法。(哈哈,这个地方,被我们逮到一个Handler类某贡献者所犯的copy-paste错误:仔细看一下sendMessageAtTime()和sendMessageAtFrontOfQueue()这两个方法,创建RuntimeException的部分,消息中都是"sendMessageAtTime()"。Handler实际的职责,并不完全如它的名称所示,在处理message外,它还要负责发送Message到MessageQueue。

注意,在Handler.enqueueMessage()中,会Messagetarget设为this,而且是不加检查,强制覆盖原来的值。后面我们将会看到,Messagetarget就是Message被dispatched到的Handler,也是将会处理这条MessageHandler。这个地方强制覆盖的逻辑表明,我们用哪个Handler来发送一条消息,那么这条消息就将会被dispatched给哪个Handler来处理,消息的发送者即接收者。这使得Handler那些obtainMessage()方法,及Message需要Handler参数的那些obtain()方法显得非常多余。通过这些方法会在获取Message时设置它的target,但这些设置又总是会在后面被无条件地覆盖掉

再来看一下MessageQueue.enqueueMessage()方法:

    boolean enqueueMessage(Message msg, long when) {if (msg.isInUse()) {throw new AndroidRuntimeException(msg + " This message is already in use.");}if (msg.target == null) {throw new AndroidRuntimeException("Message must have a target.");}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;}msg.when = when;Message p = mMessages;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.nextprev.next = msg;}}if (needWake) {nativeWake(mPtr);}return true;}

此处我们看到,MessageQueue用一个单向链表来保存所有的Messages,在链表中各个Message按照其请求执行的时间先后来排序Message插入MessageQueue的算法也还算清晰简洁,不必赘述。但此处,MessageQueue/Message的author为什么要自己实现一个单向链表,而没有用Java标准库提供的容器组件呢?是为了插入一条新的Message方便,还是仅仅为了练习怎么用Java实现单向链表?wake的逻辑后面我们会再来研究

MessageQueue中发送消息的postXXX()方法

    /*** 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));}private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;}private static Message getPostMessage(Runnable r, Object token) {Message m = Message.obtain();m.obj = token;m.callback = r;return m;}

这组方法相对于前面的sendXXX()族方法而言,其特殊之处在于,它们都需要传入一个Runnable参数,post的消息,其特殊之处也正在于,Message的callback将是传入的Runnable对象。这些特别的地方将影响这些消息在dispatch时的行为。

消息队列中消息的处理

消息队列中的消息是在Looper.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 blockif (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 loggerPrinter logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}msg.target.dispatchMessage(msg);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.recycle();}}

在取出的msg为NULL之前,消息处理循环都一直运行。为NULL的msg表明消息队列被停掉了。在这个循环中,被取出的消息会被dispatch给一个Handler处理,即是msg.target,执行Handler.dispatchMessage()方法。

注意:Looper.loop()循环体的末尾,调用了从消息队列中取出且已经被dispatched处理的Messagerecycle()方法,这表明Looper会帮我们自动回收发送到它的消息队列的消息。在我们将一条消息发送给Looper线程的消息队列之后,我们就不需要再担心消息的回收问题了,Looper自会帮我们很好的处理

接着来看Handler.dispatchMessage()

    /*** Callback interface you can use when instantiating a Handler to avoid* having to implement your own subclass of Handler.** @param msg A {@link android.os.Message Message} object* @return True if no further handling is desired*/public interface Callback {public boolean handleMessage(Message msg);}/*** Subclasses must implement this to receive messages.*/public void handleMessage(Message msg) {}/*** 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);}}private static void handleCallback(Message message) {message.callback.run();}

可以认为有三种对象可能会实际处理一条消息,分别是消息的Runnable callback,Handler的Callback mCallback和Handler对象本身。但这三种对象在获取消息的处理权方面有一定的优先级,消息的Runnable callback优先级最高,在它不为空时,只执行这个callback;优先级次高的是Handler的Callback mCallback,在它不为空时,Handler会先把消息丢给它处理,如果它不处理返回了false,Handler才会调用自己的handleMessage()来处理。

通常,Handler的handleMessage()方法通常需要override,来实现消息处理的主要逻辑。Handler的mCallback,使得开发者可以比较方便的将消息处理的逻辑和发送消息的Handler完全分开。

此处也可见post消息的特殊之处,此类消息将完全绕过Handler中用于处理消息的handleMessage() 方法,而只会执行消息的sender所实现的Runnable。

Sleep-Wakeup机制

还有一个问题,当MessageQueue中没有Messages时,Looper线程会做什么呢?它会不停地轮询,并检查消息队列中是否有消息吗?计算机科学发展到现在,闭上眼睛我们都能猜到,Looper线程一定不会去轮询的。Looper线程也确实没有去轮询消息队列。在消息队列为空时,Looper线程会去休眠,然后在消息队列中有了消息之后,再被唤醒。但这样的机制又是如何实现的呢?

Sleep-Wakeup机制所需设施的建立

我们从Sleep-Wakeup机制所需设施的建立开始。回忆前面Looper构造函数,它会创建一个MessageQueue对象,而Sleep-Wakeup机制所需设施正是在MessageQueue对象的创建过程中创建出来的。(在android消息队列机制中,消息取出和压入的主要逻辑都在MessageQueue中完成,MessageQueue实现一个定制的阻塞队列,将等待-唤醒的逻辑都放在这个类里想必也没什么让人吃惊的地方吧。)我们来看MessageQueue的构造函数:

    MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();}private native static long nativeInit();

这个方法调用nativeInit()方法来创建Sleep-Wakeup机制所需设施。来看nativeInit()的实现(在frameworks/base/core/jni/android_os_MessageQueue.cpp)

NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {mLooper = Looper::getForThread();if (mLooper == NULL) {mLooper = new Looper(false);Looper::setForThread(mLooper);}
}static jint 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<jint>(nativeMessageQueue);
}static JNINativeMethod gMessageQueueMethods[] = {/* name, signature, funcPtr */{ "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit },{ "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy },{ "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },{ "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
};

可以看到,nativeInit()所做的事情,就是创建一个NativeMessageQueue对象,在NativeMessageQueue的构造函数中,会来创建一个Looper对象。与Java层的Looper对象类似,native层的这种Looper对象也是保存在线程局部存储变量中的,每个线程一个。接着我们来看Looper类的构造函数和Looper::getForThread()函数,来了解一下,native层的线程局部存储API的用法(Looper类的实现在frameworks/native/libs/utils/Looper.cpp):

// Hint for number of file descriptors to be associated with the epoll instance.
static const int EPOLL_SIZE_HINT = 8;// Maximum number of file descriptors for which to retrieve poll events each iteration.
static const int EPOLL_MAX_EVENTS = 16;static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT;
static pthread_key_t gTLSKey = 0;Looper::Looper(bool allowNonCallbacks) :mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {int wakeFds[2];int result = pipe(wakeFds);LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);mWakeReadPipeFd = wakeFds[0];mWakeWritePipeFd = wakeFds[1];result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",errno);result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",errno);// Allocate the epoll instance and register the wake pipe.mEpollFd = epoll_create(EPOLL_SIZE_HINT);LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);struct epoll_event eventItem;memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field unioneventItem.events = EPOLLIN;eventItem.data.fd = mWakeReadPipeFd;result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",errno);
}void Looper::initTLSKey() {int result = pthread_key_create(& gTLSKey, threadDestructor);LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
}void Looper::threadDestructor(void *st) {Looper* const self = static_cast<Looper*>(st);if (self != NULL) {self->decStrong((void*)threadDestructor);}
}void Looper::setForThread(const sp<Looper>& looper) {sp<Looper> old = getForThread(); // also has side-effect of initializing TLSif (looper != NULL) {looper->incStrong((void*)threadDestructor);}pthread_setspecific(gTLSKey, looper.get());if (old != NULL) {old->decStrong((void*)threadDestructor);}
}sp<Looper> Looper::getForThread() {int result = pthread_once(& gTLSOnce, initTLSKey);LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");return (Looper*)pthread_getspecific(gTLSKey);
}

关于pthread库提供的线程局部存储API的用法,可以看到,每个线程局部存储对象,都需要一个key,通过pthread_key_create()函数创建,随后各个线程就可以通过这个key并借助于pthread_setspecific()和pthread_getspecific()函数来保存或者获取相应的线程局部存储的变量了。再来看Looper的构造函数。它创建了一个pipe,两个文件描述符。然后设置管道的两个文件描述属性为非阻塞I/O。接着是创建并设置epoll实例。由此我们了解到,android的消息队列是通过epoll机制来实现Sleep-Wakeup机制的。

唤醒

然后来看当其他线程向Looper线程的MessageQueue中插入了消息时,Looper线程是如何被叫醒的。回忆我们前面看到的MessageQueue类的enqueueMessage()方法,它在最后插入消息之后,有调用一个nativeWake()方法。没错,正是这个nativeWake()方法执行了叫醒Looper线程的动作。那它又是如何叫醒Looper线程的呢?来看它的实现:

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);return nativeMessageQueue->wake();
}// ----------------------------------------------------------------------------static JNINativeMethod gMessageQueueMethods[] = {/* name, signature, funcPtr */{ "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit },{ "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy },{ "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },{ "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
};

它只是调用了native层的Looper对象的wake()函数。接着再来看native Looper的wake()函数:

void Looper::wake() {
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ wake", this);
#endifssize_t nWrite;do {nWrite = write(mWakeWritePipeFd, "W", 1);} while (nWrite == -1 && errno == EINTR);if (nWrite != 1) {if (errno != EAGAIN) {ALOGW("Could not write wake signal, errno=%d", errno);}}
}

它所做的事情,就是向管道的用于写的那个文件中写入一个“W”字符。

休眠

Looper线程休眠的过程。我们知道,Looper线程在Looper.loop()方法中,不断地从MessageQueue中取出消息,然后处理,如此循环往复,永不止息。不难想象,休眠的时机应该是在取出消息的时候。Looper.loop()通过MessageQueue.next()从消息队列中取出消息。来看MessageQueue.next()方法:

    Message next() {int pendingIdleHandlerCount = -1; // -1 only during first iterationint 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();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 (false) Log.v("MessageQueue", "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 (mQuiting) {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 handlerboolean 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;}}

值得注意的是上面那个对于nativePollOnce()的调用。wait机制的实现正在于此。来看这个方法的实现,在native的JNI code里面:

class MessageQueue : public RefBase {
public:/* Gets the message queue's looper. */inline sp<Looper> getLooper() const {return mLooper;}/* Checks whether the JNI environment has a pending exception.** If an exception occurred, logs it together with the specified message,* and calls raiseException() to ensure the exception will be raised when* the callback returns, clears the pending exception from the environment,* then returns true.** If no exception occurred, returns false.*/bool raiseAndClearException(JNIEnv* env, const char* msg);/* Raises an exception from within a callback function.* The exception will be rethrown when control returns to the message queue which* will typically cause the application to crash.** This message can only be called from within a callback function.  If it is called* at any other time, the process will simply be killed.** Does nothing if exception is NULL.** (This method does not take ownership of the exception object reference.* The caller is responsible for releasing its reference when it is done.)*/virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) = 0;protected:MessageQueue();virtual ~MessageQueue();protected:sp<Looper> mLooper;
};class NativeMessageQueue : public MessageQueue {
public:NativeMessageQueue();virtual ~NativeMessageQueue();virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);void pollOnce(JNIEnv* env, int timeoutMillis);void wake();private:bool mInCallback;jthrowable mExceptionObj;
};void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {mInCallback = true;mLooper->pollOnce(timeoutMillis);mInCallback = false;if (mExceptionObj) {env->Throw(mExceptionObj);env->DeleteLocalRef(mExceptionObj);mExceptionObj = NULL;}
}static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz,jint ptr, jint timeoutMillis) {NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);nativeMessageQueue->pollOnce(env, timeoutMillis);
}

继续追Looper::pollOnce()的实现(在frameworks/native/libs/utils/Looper.cpp):

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {int result = 0;for (;;) {while (mResponseIndex < mResponses.size()) {const Response& response = mResponses.itemAt(mResponseIndex++);int ident = response.request.ident;if (ident >= 0) {int fd = response.request.fd;int events = response.events;void* data = response.request.data;
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - returning signalled identifier %d: ""fd=%d, events=0x%x, data=%p",this, ident, fd, events, data);
#endifif (outFd != NULL) *outFd = fd;if (outEvents != NULL) *outEvents = events;if (outData != NULL) *outData = data;return ident;}}if (result != 0) {
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - returning result %d", this, result);
#endifif (outFd != NULL) *outFd = 0;if (outEvents != NULL) *outEvents = 0;if (outData != NULL) *outData = NULL;return result;}result = pollInner(timeoutMillis);}
}int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif// Adjust the timeout based on when the next message is due.if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);if (messageTimeoutMillis >= 0&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {timeoutMillis = messageTimeoutMillis;}
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",this, mNextMessageUptime - now, timeoutMillis);
#endif}// Poll.int result = ALOOPER_POLL_WAKE;mResponses.clear();mResponseIndex = 0;struct epoll_event eventItems[EPOLL_MAX_EVENTS];int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);// Acquire lock.mLock.lock();// Check for poll error.if (eventCount < 0) {if (errno == EINTR) {goto Done;}ALOGW("Poll failed with an unexpected error, errno=%d", errno);result = ALOOPER_POLL_ERROR;goto Done;}// Check for poll timeout.if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - timeout", this);
#endifresult = ALOOPER_POLL_TIMEOUT;goto Done;}// Handle all events.
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endiffor (int i = 0; i < eventCount; i++) {int fd = eventItems[i].data.fd;uint32_t epollEvents = eventItems[i].events;if (fd == mWakeReadPipeFd) {if (epollEvents & EPOLLIN) {awoken();} else {ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);}} else {ssize_t requestIndex = mRequests.indexOfKey(fd);if (requestIndex >= 0) {int events = 0;if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP;pushResponse(events, mRequests.valueAt(requestIndex));} else {ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is ""no longer registered.", epollEvents, fd);}}}
Done: ;// Invoke pending message callbacks.mNextMessageUptime = LLONG_MAX;while (mMessageEnvelopes.size() != 0) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);if (messageEnvelope.uptime <= now) {// Remove the envelope from the list.// We keep a strong reference to the handler until the call to handleMessage// finishes.  Then we drop it so that the handler can be deleted *before*// we reacquire our lock.{ // obtain handlersp<MessageHandler> handler = messageEnvelope.handler;Message message = messageEnvelope.message;mMessageEnvelopes.removeAt(0);mSendingMessage = true;mLock.unlock();#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKSALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",this, handler.get(), message.what);
#endifhandler->handleMessage(message);} // release handlermLock.lock();mSendingMessage = false;result = ALOOPER_POLL_CALLBACK;} else {// The last message left at the head of the queue determines the next wakeup time.mNextMessageUptime = messageEnvelope.uptime;break;}}// Release lock.mLock.unlock();// Invoke all response callbacks.for (size_t i = 0; i < mResponses.size(); i++) {Response& response = mResponses.editItemAt(i);if (response.request.ident == ALOOPER_POLL_CALLBACK) {int fd = response.request.fd;int events = response.events;void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKSALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",this, response.request.callback.get(), fd, events, data);
#endifint callbackResult = response.request.callback->handleEvent(fd, events, data);if (callbackResult == 0) {removeFd(fd);}// Clear the callback reference in the response structure promptly because we// will not clear the response vector itself until the next poll.response.request.callback.clear();result = ALOOPER_POLL_CALLBACK;}}return result;
}

它通过调用epoll_wait()函数来等待消息的到来。

HandlerThread的退出

HandlerThread或使用Looper/MessageQueue自定义的类似东西,必须在不需要时被停掉。如前所见,每次创建MessageQueue,都会占用好几个描述符,但在Linux/Android上,一个进程所能打开的文件描述符的最大个数是有限制的,大多为1024个。如果一个app打开的文件描述符达到了这个上限,则将会出现许多各式各样的古怪问题,像进程间通信,socket,输入系统等很多机制,都会依赖于类文件的东西。同时,HandlerThread也总会占用一个线程。这些资源都是只有在显式地停掉之后才会被释放的。

我们来看一下HandlerThread的退出机制。先来看一个使用了HandlerThread,并适时地退出的例子,代码在frameworks/base/core/java/android/app/IntentService.java:

    @Overridepublic void onCreate() {// TODO: It would be nice to have an option to hold a partial wakelock// during processing, and to have a static startService(Context, Intent)// method that would launch the service & hand off a wakelock.super.onCreate();HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");thread.start();mServiceLooper = thread.getLooper();mServiceHandler = new ServiceHandler(mServiceLooper);}@Overridepublic void onStart(Intent intent, int startId) {Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}/*** You should not override this method for your IntentService. Instead,* override {@link #onHandleIntent}, which the system calls when the IntentService* receives a start request.* @see android.app.Service#onStartCommand*/@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}@Overridepublic void onDestroy() {mServiceLooper.quit();}

在这个例子中,ServiceonCreate()方法创建了HandlerThread,保存了HandlerThreadLooper,然后在ServiceonDestroy()方法中停掉了Looper,实际上也即是退出了HandlerThread

来看一下Looper停止方法具体的实现:

    /*** Quits the looper.* <p>* Causes the {@link #loop} method to terminate without processing any* more messages in the message queue.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p><p class="note">* Using this method may be unsafe because some messages may not be delivered* before the looper terminates.  Consider using {@link #quitSafely} instead to ensure* that all pending work is completed in an orderly manner.* </p>** @see #quitSafely*/public void quit() {mQueue.quit(false);}/*** Quits the looper safely.* <p>* Causes the {@link #loop} method to terminate as soon as all remaining messages* in the message queue that are already due to be delivered have been handled.* However pending delayed messages with due times in the future will not be* delivered before the loop terminates.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p>*/public void quitSafely() {mQueue.quit(true);}

Looper有提供两个退出方法,quit()quitSafely(),这两个方法都会阻止再向消息队列中发送消息。但它们的主要区别在于,前者不会再处理消息队列中还没有被处理的所有消息,而后者则会在处理完那些已经到期的消息之后才真的退出。

由前面我们对Looper.loop()方法的分析,也不难理解,MessageQueue退出即Looper退出。此处也是直接调用了MessageQueue.quit()方法。那我们就来看一下MessageQueue的quit()方法:

    void quit(boolean safe) {if (!mQuitAllowed) {throw new RuntimeException("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);}}

主要做了几个事情:第一,设置标记mQuittingtrue,以表明HandlerThread要退出了;第二,根据传入的参数safe,删除队列里面适当类型的消息;第三,调用nativeWake(mPtr),将HandlerThread线程唤醒。我们都知道,Java里面是没有办法直接终止另外一个线程的,同时强制中止一个线程也是很不安全的,所以,这里也是只设置一个标记,然后在MessageQueue.next()中获取消息时检查此标记,以在适当的时候退出。MessageQueue.next()中止消息处理循环的,主要是下面这几行:

                // Process the quit message now that all pending messages have been handled.if (mQuitting) {dispose();return null;}

然后在 MessageQueue.dispose()方法中完成最终的退出,及资源清理的工作:

    private void dispose() {if (mPtr != 0) {nativeDestroy(mPtr);mPtr = 0;}}

android中消息队列机制,大体如此。

Done。

转载于:https://my.oschina.net/wolfcs/blog/160601

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/547411.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KNN算法检测手势动作

KNN算法原理&#xff1a; KNN&#xff08;k-nearest neighbor&#xff09;是一个简单而经典的机器学习分类算法&#xff0c;通过度量”待分类数据”和”类别已知的样本”的距离&#xff08;通常是欧氏距离&#xff09;对样本进行分类。 这话说得有些绕口&#xff0c;且来分解…

IIS负载均衡(转)

在大型Web应用系统中&#xff0c;由于请求的数据量过大以及并发的因素&#xff0c;导致Web系统会出现宕机的现象&#xff0c;解决这一类问题的方法我个人觉得主要在以下几个方面&#xff1a; 1.IIS 负载均衡。 2.数据库 负载均衡。 3.系统架构优化&#xff0c;比如报表服务器…

maven报错Non-resolvable parent POM for com.wpbxin:springboot2-first-example:0.0.1-SNAPSHOT: Could not

文章目录 一、maven报错二、一些说明三、出现问题的原因和几种解决方法 忽略SSL证书检查生成证书并导入到 JRE security 中使用默认的 maven 中央仓库使用 http 的镜像库四、参考链接 记录使用 maven 时遇到的问题。第一种方法最方便&#xff0c;亲测能用。 一、maven报错 mav…

计算机科技与技术对应岗位,计算机技术与软件专业技术资格名称及岗位基本任职条件...

超越梦想&#xff1a;计算机技术与软件专业技术资格名称及岗位基本任职条件专业资格名称对应专业技术职务等级学位或学历要求资历要求信息系统项目管理师、系统分析师、系统架构设计师、网络规划设计师、系统规划与管理师高级博士学位聘任工程师满2年硕士学位聘任工程师满4年本…

Intellij idea 出现错误 error:java: 无效的源发行版: 11解决方法

Select the project, then File > ProjectStructure > ProjectSettings > Modules -> sources You probably have the Language Level set at 9: Just change it to 8 借用下别人的图片&#xff0c; 我的默认是11报的错&#xff0c; 改成8后就好了

计算机房机柜标准尺寸,有哪些参数和尺寸符合机房机柜的安装要求

现如今服务器机房越来越多&#xff0c;不管是自己托管服务器还是租用服务器&#xff0c;机房机柜的选择是很重要的。机房机柜也会跟其高度厚度尺寸等相关。对于需要运行环境和要求高的机房&#xff0c;还需要选择有智能系统的机柜才行。还需要根据一些机房机柜的参数来考虑是否…

9月第1周国内IT技术类网站:CSDN覆盖数持续走低

根据国际统计机构Alexa公布的最新数据显示&#xff0c;9月第1周&#xff08;2013-09-02至2013-09-08&#xff09;&#xff0c;国内IT技术类网站排行榜中&#xff0c;CSDN以1710居于榜首&#xff0c;第二位是1170的博客园&#xff0c;第三位是670的51CTO。下面是具体情况&#x…

YOLOv3改进方法增加特征尺度和训练层数

YOLOv3改进方法 YOLOv3的改进方法有很多&#xff0c;本文讲述的是增加一个特征尺度。 以YOLOv3-darknet53&#xff08;ALexeyAB版本&#xff09;为基础&#xff0c;增加了第4个特征尺度&#xff1a;104*104。原版YOLOv3网络结构&#xff1a; YOLOv3-4l网络结构&#xff1a; 即…

uva 610(tarjan的应用)

题目链接&#xff1a;http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id23727 思路&#xff1a;首先是Tarjan找桥&#xff0c;对于桥&#xff0c;只能是双向边&#xff0c;而对于同一个连通分量而言&#xff0c;只要重新定向为同一个方向即可。 1 #include<ios…

Win7搭建NodeJs开发环境以及HelloWorld展示—图解

Windows 7系统下搭建NodeJs开发环境&#xff08;NodeJsWebStrom&#xff09;以及Hello World&#xff01;展示&#xff0c;大体思路如下&#xff1a;第一步&#xff1a;安装NodeJs运行环境。第二步&#xff1a;安装WebStrom开发工具。第三步&#xff1a;创建并运行NodeJs项目展…

计算机一级windows7操作,计算机等级一级:Windows7应用之小技巧

为了帮助广大考生更好的复习&#xff0c;帮考网综合整理提供了计算机等级考试一级微机知识:Windows7应用之小技巧&#xff0c;以供各位考生复习参考&#xff0c;希望对考生复习有所帮助。从年初开始&#xff0c;断断续续的&#xff0c;windows7使用也有一段时间&#xff0c;碰到…

MySQL5.6忘记root密码(win平台)

1、首先net stop mysql服务&#xff0c;并且切换到任务管理器&#xff0c;有与mysql有关的&#xff0c;最好关闭进程。 2、运行CMD命令切换到MySql安装bin目录&#xff0c;下面是我的mysql安装目录 cd C:\Program Files\MySQL\MySQL Server 5.6\bin 接着执行mysqld --skip-gra…

单片机四位数加减计算机程序,51单片机简易计算器程序 实现数字的加减乘除运算...

//头文件#define uint unsigned int#define uchar unsigned charsbit lcdenP1^1; //LCD1602控制引脚sbit rsP1^0;sbit rwP1^2;sbit busyP0^7;//LCD忙char i,j,temp,num,num_1;long a,b,c; //a,第一个数 b,第二个数 c,得数float a_c,b_c;uchar flag,fuhao;//flag表示是否有运…

在pandas中遍历DataFrame行

有如下 Pandas DataFrame&#xff1a; import pandas as pd inp [{c1:10, c2:100}, {c1:11,c2:110}, {c1:12,c2:120}] df pd.DataFrame(inp) print df 上面代码输出&#xff1a; c1 c2 0 10 100 1 11 110 2 12 120 现在需要遍历上面DataFrame的行。对于每一行&#x…

MySQL索引的Index method中btree和hash的区别

2019独角兽企业重金招聘Python工程师标准>>> 在MySQL中&#xff0c;大多数索引&#xff08;如 PRIMARY KEY,UNIQUE,INDEX和FULLTEXT&#xff09;都是在BTREE中存储&#xff0c;但使用memory引擎可以选择BTREE索引或者HASH索引&#xff0c;两种不同类型的索引各自有其…

Oracle 11g数据库管理与开发指南

为什么80%的码农都做不了架构师&#xff1f;>>> Oracle 11g数据库管理与开发指南 本书内容系统而全面&#xff0c;详细讲解了Oracle数据库的管理、开发、性能优化、以及PL/SQL 编程的相关知识&#xff0c;面面俱到&#xff1b;注重实战&#xff0c;为每个知识…

SQL Server 跨网段(跨机房)复制

一.本文所涉及的内容&#xff08;Contents&#xff09; 本文所涉及的内容&#xff08;Contents&#xff09;背景&#xff08;Contexts&#xff09;解决方案&#xff08;Solution&#xff09;搭建过程&#xff08;Process&#xff09;注意事项&#xff08;Attention&#xff09;…

Twitter Storm安装配置(Ubuntu系统)单机版

要使用storm首先要安装以下工具&#xff1a;JDK、Python、zookeeper、zeromq、jzmq、storm &#xff08;注&#xff1a;各个模块都是独立的&#xff0c;如果安装失败或者卡顿可以单独百度某个模块的安装&#xff0c;都是可以的。&#xff09; JDK1.7安装 参见上篇《Ubuntu下安…

计算机中隐藏的文件找不到了怎么办,隐藏文件夹找不到了怎么办【图解】

在今天&#xff0c;越来越多的人利用电脑来办公。甚至有一些工作&#xff0c;已经与电脑密切相关了。电脑以其强大的功能辅助人们工作&#xff0c;但有时在操作过程中会碰到这样或那样的问题。这个时候人们就会叹其操作的复杂性。就好比如有时你隐藏了一个文件&#xff0c;可是…

CentOS6 配置 yum 阿里源

一、CentOS6停止维护更新日期2020年11月30日 二、2020年12月2日下架了包括官方所有的CentOS6源&#xff08;包括国内的镜像站&#xff09; 三、CentOS6也完成了它的历史使命 在生产的环境中依旧由很多CentOS6系统依然在坚持&#xff0c;那么可以采取挂载光盘或者使用centos-va…