消息机制
参照改编 : http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html
对于这部分的内容,将分成4小节来描述:
1.职责与关系
2.消息循环
3.线程与更新
4.几点小结
--------------------------------------------------------------------------------------------------
1) 接下来,我们开始这部分的内容,首先了解一下各自的职责及相互之间的关系。
职责
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息。与链表相似,由messge.next属性相连。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
关系
Handler,Looper和MessageQueue就是简单的三角关系。Looper和MessageQueue一一对应,创建一个Looper的同时,会创建一个MessageQueue。而Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。
这样说来,多个Handler都可以共享同一Looper和MessageQueue了。当然,这些Handler也就运行在同一个线程里。不同线程可以使用同一个mHandler变量,在其他线程中可以 mHandler.sendMessage(msg),即可将msg压入到定义mHandler的那个线程的MessageQueue中。
2) 接下来,我们简单地看一下消息的循环过程:生成
- Message msg = mHandler.obtainMessage();
- msg.what = what;
- msg.sendToTarget();
- In Handler.class
- public final Message obtainMessage()
- {
- return Message.obtain(this);
- }
- In Message.class
- public static Message obtain(Handler h) {
- Message m = obtain();
- m.target = h;
- return m;
- }
发送
- In Message.class
- public void sendToTarget() {
- target.sendMessage(this);
- }
- In Handler.class
- public final boolean sendMessage(Message msg)
- {
- return sendMessageDelayed(msg, 0);
- }
- /**
- * 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)
- {
- 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发送到MessageQueue中.
加入MessageQueue,在MessageQueue中的排序
- In MessageQueue.class
- final boolean enqueueMessage(Message msg, long when) {
- if (msg.when != 0) {
- throw new AndroidRuntimeException(msg
- + " This message is already in use.");
- }
- if (msg.target == null && !mQuitAllowed) {
- throw new RuntimeException("Main thread not allowed to quit");
- }
- 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;
- this.notify();
- } else {
- Message prev = null;
- while (p != null && p.when <= when) {
- prev = p;
- p = p.next;
- }
- msg.next = prev.next;
- prev.next = msg;
- this.notify();
- }
- }
- return true;
- }
抽取
- In Looper.class
- Looper me = myLooper();
- MessageQueue queue = me.mQueue;
- 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;
- }
- msg.target.dispatchMessage(msg);
- msg.recycle();
- }
- }
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- public static final Looper myLooper() {
- return (Looper)sThreadLocal.get();
- }
在Looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next方法)Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。
处理
- In Handler.class
- /**
- * 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.java的dispatchMessage(Message msg)方法里,其中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描述使用Handler时需要实现handleMessage(Message msg)的原因。
至于dispatchMessage方法中的另外一个分支handlerCallback(msg),用方法handler.post(runnable)时,运行这个分支:
- In Handler.class
- /**
- * 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);
- }
- private final Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
- private final void handleCallback(Message message) {
- message.callback.run();
- }
将一个Runnable封装到msg.callback属性中。
至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。
3)剩下的部分,我们将讨论一下Handler所处的线程及更新UI的方式。在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在这种情况下,通用的作法是:
- class LooperThread extends Thread {
- public Handler mHandler;
- public void run() {
- Looper.prepare();
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- // process incoming messages here
- }
- };
- Looper.loop();
- }
- }
原因是:在prepare()中,为当前线程创建了一个Looper 和 MessageQueue,并保存在 ThreadLocal对象中(ThreadLocal对象保存的是当前线程的局部变量,是与该线程相对应的),再loop(),即可为不断抽取当前线程的messge.
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper());
- }
- private Looper() {
- mQueue = new MessageQueue();
- mRun = true;
- mThread = Thread.currentThread();
- }
sThreadLocal.set(new Looper()),为当前线程创建对应的Looper对象,并存储起来
关于ThreadLocal : http://blog.csdn.net/qjyong/article/details/2158097
在创建Handler之前,为该线程准备好一个Looper(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。
因此,Handler处理消息总是在创建Handler的线程里运行。而我们的消息处理中,不乏更新UI的操作,不正确的线程直接更新UI将引发异常。因此,需要时刻关心Handler在哪个线程里创建的。
如何更新UI才能不出异常呢?SDK告诉我们,有以下4种方式可以从其它线程访问UI线程:
· Activity.runOnUiThread(Runnable)
· View.postDelayed(Runnable, long)
· Handler
其中,重点说一下的是View.post(Runnable)方法。在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。
4) 几点小结
· Handler的处理过程运行在创建Handler的线程里
· 一个Looper对应一个MessageQueue
· 一个线程对应一个Looper
· 一个Looper可以对应多个Handler
· 不确定当前线程时,更新UI时尽量调用post方法
以View.Click事件为例子,其步骤如下:onTouchEvent ----> Action_Up --->handler. post(new PerformClick()) , class PerformClick implements Runnable{ view.click;} ----> messageQueue.enqueue(msg) ----> Looper.loop() ------> msg.target.dispathMessage() -------> performClick.run();
- /**
- * Implement this method to handle touch screen motion events.
- *
- * @param event The motion event.
- * @return True if the event was handled, false otherwise.
- */
- public boolean onTouchEvent(MotionEvent event) {
- final int viewFlags = mViewFlags;
- if ((viewFlags & ENABLED_MASK) == DISABLED) {
- // A disabled view that is clickable still consumes the touch
- // events, it just doesn't respond to them.
- return (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
- }
- if (mTouchDelegate != null) {
- if (mTouchDelegate.onTouchEvent(event)) {
- return true;
- }
- }
- if (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
- if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
- // take focus if we don't have it already and we should in
- // touch mode.
- boolean focusTaken = false;
- if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
- focusTaken = requestFocus();
- }
- if (!mHasPerformedLongPress) {
- // This is a tap, so remove the longpress check
- removeLongPressCallback();
- // Only perform take click actions if we were in the pressed state
- if (!focusTaken) {
- // Use a Runnable and post this rather than calling
- // performClick directly. This lets other visual state
- // of the view update before click actions start.
- if (mPerformClick == null) {
- mPerformClick = new PerformClick();
- }
- if (!post(mPerformClick)) {
- performClick();
- }
- }
- }
- if (mUnsetPressedState == null) {
- mUnsetPressedState = new UnsetPressedState();
- }
- if (prepressed) {
- mPrivateFlags |= PRESSED;
- refreshDrawableState();
- postDelayed(mUnsetPressedState,
- ViewConfiguration.getPressedStateDuration());
- } else if (!post(mUnsetPressedState)) {
- // If the post failed, unpress right now
- mUnsetPressedState.run();
- }
- removeTapCallback();
- }
- break;
- case MotionEvent.ACTION_DOWN:
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- mPrivateFlags |= PREPRESSED;
- mHasPerformedLongPress = false;
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
- break;
- case MotionEvent.ACTION_CANCEL:
- mPrivateFlags &= ~PRESSED;
- refreshDrawableState();
- removeTapCallback();
- break;
- case MotionEvent.ACTION_MOVE:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- // Be lenient about moving outside of buttons
- int slop = mTouchSlop;
- if ((x < 0 - slop) || (x >= getWidth() + slop) ||
- (y < 0 - slop) || (y >= getHeight() + slop)) {
- // Outside button
- removeTapCallback();
- if ((mPrivateFlags & PRESSED) != 0) {
- // Remove any future long press/tap checks
- removeLongPressCallback();
- // Need to switch from pressed to not pressed
- mPrivateFlags &= ~PRESSED;
- refreshDrawableState();
- }
- }
- break;
- }
- return true;
- }
- return false;
- }
- private final class PerformClick implements Runnable {
- public void run() {
- performClick();
- }
- }
- /**
- * Call this view's OnClickListener, if it is defined.
- *
- * @return True there was an assigned OnClickListener that was called, false
- * otherwise is returned.
- */
- public boolean performClick() {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
- if (mOnClickListener != null) {
- playSoundEffect(SoundEffectConstants.CLICK);
- mOnClickListener.onClick(this);
- return true;
- }
- return false;
- }
理解了这些其实就不难想象ANR是如何实现的了。当用户执行操作(比如点击了一个按钮)系统会生成一个Message对象,把用户操作的信息写入Message对象,并把这个Message对象压入MessageQueue队列的头部。系统过一段时间(一般是五秒)后会再来检查,刚刚放入的信息是不是已经被处理了,如果信息还在队列中就表明。处理前面信息的过程当中发生的阻塞,用户的操作没有及时得到响应。系统弹出ANR对话框。
作个总结:
因为UI线程需要保持一直运行的状态,所以要有一个循环保持这个线程不会死掉,但这个线程又必需阻塞,以减少cpu的消耗。android中的这个循环就是通过Looper实现的。有了这个 Looper,Looper就占据了整个线程,导致所有的方法想在些线程中运行就必需通过这个Looper,所以要有个方法可以进入这个Looper的内部。MessageQueue就担当了这个通道 的角色。Message担当了集合的角色。所有在UI线程中运行的方法都必需通过MessageQueue进入Looper内部,不管 是用户定义的方法还是系统事件包括onCreate(),onStop(),用户点击事件etc..
- final Message next() {
- boolean tryIdle = true;
- while (true) {
- long now;
- Object[] idlers = null;
- // Try to retrieve the next message, returning if found.
- synchronized (this) {
- now = SystemClock.uptimeMillis();
- Message msg = pullNextLocked(now);
- if (msg != null) return msg;
- if (tryIdle && mIdleHandlers.size() > 0) {
- idlers = mIdleHandlers.toArray();
- }
- }
- // There was no message so we are going to wait... but first,
- // if there are any idle handlers let them know.
- boolean didIdle = false;
- if (idlers != null) {
- for (Object idler : idlers) {
- boolean keep = false;
- try {
- didIdle = true;
- keep = ((IdleHandler)idler).queueIdle();
- } catch (Throwable t) {
- Log.wtf("MessageQueue", "IdleHandler threw exception", t);
- }
- if (!keep) {
- synchronized (this) {
- mIdleHandlers.remove(idler);
- }
- }
- }
- }
- // While calling an idle handler, a new message could have been
- // delivered... so go back and look again for a pending message.
- if (didIdle) {
- tryIdle = false;
- continue;
- }
- synchronized (this) {
- // No messages, nobody to tell about it... time to wait!
- try {
- if (mMessages != null) {
- if (mMessages.when-now > 0) {
- Binder.flushPendingCommands();
- this.wait(mMessages.when-now);
- }
- } else {
- Binder.flushPendingCommands();
- this.wait();
- }
- }
- catch (InterruptedException e) {
- }
- }
- }
- }
在next()中,在最后的synchronized(this)中,if(mMessages != null){ ..... } else{ ...this.wait();....} ,将UI线程阻塞;
- final boolean enqueueMessage(Message msg, long when) {
- if (msg.when != 0) {
- throw new AndroidRuntimeException(msg
- + " This message is already in use.");
- }
- if (msg.target == null && !mQuitAllowed) {
- throw new RuntimeException("Main thread not allowed to quit");
- }
- 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;
- this.notify();
- } else {
- Message prev = null;
- while (p != null && p.when <= when) {
- prev = p;
- p = p.next;
- }
- msg.next = prev.next;
- prev.next = msg;
- this.notify();
- }
- }
- return true;
- }
- /**
- * Runs the specified action on the UI thread. If the current thread is the UI
- * thread, then the action is executed immediately. If the current thread is
- * not the UI thread, the action is posted to the event queue of the UI thread.
- *
- * @param action the action to run on the UI thread
- */
- public final void runOnUiThread(Runnable action) {
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(action);
- } else {
- action.run();
- }
- }
- private Button mButton;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mButton = (Button) findViewById(R.id.btn);
- mButton.setOnClickListener(new Button.OnClickListener(){
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- Log.i("TAG","button.onclick");
- Thread thread1 = new Thread(runnable1);
- thread1.start();
- }});
- }
- private Handler mHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- super.handleMessage(msg);
- }
- @Override
- public void dispatchMessage(Message msg) {
- // TODO Auto-generated method stub
- Thread thread = Thread.currentThread();
- Log.i("TAG","dispatchMessage(); threadName = " + thread.getName());
- super.dispatchMessage(msg);
- }
- @Override
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
- // TODO Auto-generated method stub
- Thread thread = Thread.currentThread();
- Log.i("TAG","sendMessageAtTime(); threadName = " + thread.getName());
- return super.sendMessageAtTime(msg, uptimeMillis);
- }
- };
- private Runnable runnable1 = new Runnable(){
- @Override
- public void run() {
- Thread thread = Thread.currentThread();
- Log.i("TAG","runnable1.run(); threadName = " + thread.getName());
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- mHandler.sendEmptyMessage(1);
- }
- };
- 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;
- }
结果:
很显然,在thread-8中,mHandler将msg发送到主线程的Message Queue,主线程被notify,然后取出msg,由mHandler dispatchMessage,然后在主线程 handle msg.
其实在handler中,有一个mLooper,mQueue,其在默认状态下,是对应于定义handler那个的线程的

浙公网安备 33010602011771号