消息机制

Android中的Handler, Looper, MessageQueue和Thread

分类: Android 理論知識

参照改编 : 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) 接下来,我们简单地看一下消息的循环过程:

生成

 

[java] view plaincopy
 
  1. Message msg = mHandler.obtainMessage();  
  2.      msg.what = what;  
  3.      msg.sendToTarget();  

 

 

[java] view plaincopy
 
  1. In Handler.class   
[java] view plaincopy
 
  1. public final Message obtainMessage()  
  2.     {  
  3.         return Message.obtain(this);  
  4.     }  

 

 

[java] view plaincopy
 
  1. In Message.class  
[java] view plaincopy
 
  1. public static Message obtain(Handler h) {  
  2.         Message m = obtain();  
  3.         m.target = h;  
  4.   
  5.         return m;  
  6.     }  

 

 

发送
[java] view plaincopy
 
  1. In Message.class  
[java] view plaincopy
 
  1. public void sendToTarget() {  
  2.         target.sendMessage(this);  
  3.     }  
[java] view plaincopy
 
  1. In Handler.class    
[java] view plaincopy
 
  1. public final boolean sendMessage(Message msg)  
  2.     {  
  3.         return sendMessageDelayed(msg, 0);  
  4.     }  
  5.   
  6.     /** 
  7.      * Enqueue a message into the message queue after all pending messages 
  8.      * before (current time + delayMillis). You will receive it in 
  9.      * {@link #handleMessage}, in the thread attached to this handler. 
  10.      *   
  11.      * @return Returns true if the message was successfully placed in to the  
  12.      *         message queue.  Returns false on failure, usually because the 
  13.      *         looper processing the message queue is exiting.  Note that a 
  14.      *         result of true does not mean the message will be processed -- if 
  15.      *         the looper is quit before the delivery time of the message 
  16.      *         occurs then the message will be dropped. 
  17.      */  
  18.     public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  19.     {  
  20.         if (delayMillis < 0) {  
  21.             delayMillis = 0;  
  22.         }  
  23.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  24.     }  
  25.   
  26.     /** 
  27.      * Enqueue a message into the message queue after all pending messages 
  28.      * before the absolute time (in milliseconds) <var>uptimeMillis</var>. 
  29.      * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> 
  30.      * You will receive it in {@link #handleMessage}, in the thread attached 
  31.      * to this handler. 
  32.      *  
  33.      * @param uptimeMillis The absolute time at which the message should be 
  34.      *         delivered, using the 
  35.      *         {@link android.os.SystemClock#uptimeMillis} time-base. 
  36.      *          
  37.      * @return Returns true if the message was successfully placed in to the  
  38.      *         message queue.  Returns false on failure, usually because the 
  39.      *         looper processing the message queue is exiting.  Note that a 
  40.      *         result of true does not mean the message will be processed -- if 
  41.      *         the looper is quit before the delivery time of the message 
  42.      *         occurs then the message will be dropped. 
  43.      */  
  44.     public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  45.     {  
  46.         boolean sent = false;  
  47.         MessageQueue queue = mQueue;  
  48.         if (queue != null) {  
  49.             msg.target = this;  
  50.             sent = queue.enqueueMessage(msg, uptimeMillis);  
  51.         }  
  52.         else {  
  53.             RuntimeException e = new RuntimeException(  
  54.                 this + " sendMessageAtTime() called with no mQueue");  
  55.             Log.w("Looper", e.getMessage(), e);  
  56.         }  
  57.         return sent;  
  58.     }  

将Message发送到MessageQueue中.

加入MessageQueue,在MessageQueue中的排序

 

[java] view plaincopy
 
  1. In MessageQueue.class  
[java] view plaincopy
 
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.         if (msg.when != 0) {  
  3.             throw new AndroidRuntimeException(msg  
  4.                     + " This message is already in use.");  
  5.         }  
  6.         if (msg.target == null && !mQuitAllowed) {  
  7.             throw new RuntimeException("Main thread not allowed to quit");  
  8.         }  
  9.         synchronized (this) {  
  10.             if (mQuiting) {  
  11.                 RuntimeException e = new RuntimeException(  
  12.                     msg.target + " sending message to a Handler on a dead thread");  
  13.                 Log.w("MessageQueue", e.getMessage(), e);  
  14.                 return false;  
  15.             } else if (msg.target == null) {  
  16.                 mQuiting = true;  
  17.             }  
  18.   
  19.             msg.when = when;  
  20.             //Log.d("MessageQueue", "Enqueing: " + msg);  
  21.             Message p = mMessages;  
  22.             if (p == null || when == 0 || when < p.when) {  
  23.                 msg.next = p;  
  24.                 mMessages = msg;  
  25.                 this.notify();  
  26.             } else {  
  27.                 Message prev = null;  
  28.                 while (p != null && p.when <= when) {  
  29.                     prev = p;  
  30.                     p = p.next;  
  31.                 }  
  32.                 msg.next = prev.next;  
  33.                 prev.next = msg;  
  34.                 this.notify();  
  35.             }  
  36.         }  
  37.         return true;  
  38.     }  
可以看到,排序的方法是:根据msg.when排序,如果现在就要执行的,就排在最前面;否则,就插入到适当的位置,相似与链表的排序规则

 

抽取

 

[java] view plaincopy
 
  1. In Looper.class     
[java] view plaincopy
 
  1. Looper me = myLooper();  
  2.        MessageQueue queue = me.mQueue;  
  3.        while (true) {  
  4.            Message msg = queue.next(); // might block  
  5.            if (msg != null) {  
  6.                if (msg.target == null) {  
  7.                    // No target is a magic identifier for the quit message.  
  8.                    return;  
  9.                }  
  10.                msg.target.dispatchMessage(msg);  
  11.                msg.recycle();  
  12.            }  
  13.        }  

 

[java] view plaincopy
 
  1. /** 
  2.  * Return the Looper object associated with the current thread.  Returns 
  3.  * null if the calling thread is not associated with a Looper. 
  4.  */  
  5. public static final Looper myLooper() {  
  6.     return (Looper)sThreadLocal.get();  
  7. }  


 

在Looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next方法)Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。

 

处理

 

[java] view plaincopy
 
  1. In Handler.class   
[java] view plaincopy
 
  1. /** 
  2.      * Handle system messages here. 
  3.      */  
  4.     public void dispatchMessage(Message msg) {  
  5.         if (msg.callback != null) {  
  6.             handleCallback(msg);  
  7.         } else {  
  8.             if (mCallback != null) {  
  9.                 if (mCallback.handleMessage(msg)) {  
  10.                     return;  
  11.                 }  
  12.             }  
  13.             handleMessage(msg);  
  14.         }  
  15.     }  

 

在Handler.java的dispatchMessage(Message msg)方法里,其中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描述使用Handler时需要实现handleMessage(Message msg)的原因。

至于dispatchMessage方法中的另外一个分支handlerCallback(msg),用方法handler.post(runnable)时,运行这个分支:

 

[java] view plaincopy
 
  1. In Handler.class   
[java] view plaincopy
 
  1. /** 
  2.      * Causes the Runnable r to be added to the message queue. 
  3.      * The runnable will be run on the thread to which this handler is  
  4.      * attached.  
  5.      *   
  6.      * @param r The Runnable that will be executed. 
  7.      *  
  8.      * @return Returns true if the Runnable was successfully placed in to the  
  9.      *         message queue.  Returns false on failure, usually because the 
  10.      *         looper processing the message queue is exiting. 
  11.      */  
  12.     public final boolean post(Runnable r)  
  13.     {  
  14.        return  sendMessageDelayed(getPostMessage(r), 0);  
  15.     }  
  16.       
  17.    
  18. private final Message getPostMessage(Runnable r) {  
  19.         Message m = Message.obtain();  
  20.         m.callback = r;  
  21.         return m;  
  22.     }  

 

[java] view plaincopy
 
  1. private final void handleCallback(Message message) {  
  2.        message.callback.run();  
  3.    }  
注意,此时message.callback.run()方法,而不是new Thread(message.callback.).start().因此,还是在MainThread中运行callback

 

 

将一个Runnable封装到msg.callback属性中。

 

至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

3)剩下的部分,我们将讨论一下Handler所处的线程及更新UI的方式。

在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在这种情况下,通用的作法是:

 

[java] view plaincopy
 
  1. class LooperThread extends Thread {  
  2.                public Handler mHandler;  
  3.                public void run() {  
  4.                         Looper.prepare();  
  5.                         mHandler = new Handler() {  
  6.                                public void handleMessage(Message msg) {  
  7.                                  // process incoming messages here  
  8.                                 }  
  9.                         };  
  10.                     Looper.loop();  
  11.                  }  
  12.             }  

原因是:在prepare()中,为当前线程创建了一个Looper 和 MessageQueue,并保存在 ThreadLocal对象中(ThreadLocal对象保存的是当前线程的局部变量,是与该线程相对应的),再loop(),即可为不断抽取当前线程的messge.

 

[java] view plaincopy
 
  1.   
[java] view plaincopy
 
  1. public static final void prepare() {  
  2.     if (sThreadLocal.get() != null) {  
  3.         throw new RuntimeException("Only one Looper may be created per thread");  
  4.     }  
  5.     sThreadLocal.set(new Looper());  
  6. }  

[java] view plaincopy
 
  1. private Looper() {  
  2.     mQueue = new MessageQueue();  
  3.     mRun = true;  
  4.     mThread = Thread.currentThread();  
  5. }  

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.post(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();

 

 

[java] view plaincopy
 
  1. /** 
  2.     * Implement this method to handle touch screen motion events. 
  3.     * 
  4.     * @param event The motion event. 
  5.     * @return True if the event was handled, false otherwise. 
  6.     */  
  7.    public boolean onTouchEvent(MotionEvent event) {  
  8.        final int viewFlags = mViewFlags;  
  9.   
  10.        if ((viewFlags & ENABLED_MASK) == DISABLED) {  
  11.            // A disabled view that is clickable still consumes the touch  
  12.            // events, it just doesn't respond to them.  
  13.            return (((viewFlags & CLICKABLE) == CLICKABLE ||  
  14.                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));  
  15.        }  
  16.   
  17.        if (mTouchDelegate != null) {  
  18.            if (mTouchDelegate.onTouchEvent(event)) {  
  19.                return true;  
  20.            }  
  21.        }  
  22.   
  23.        if (((viewFlags & CLICKABLE) == CLICKABLE ||  
  24.                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
  25.            switch (event.getAction()) {  
  26.              case MotionEvent.ACTION_UP:  
  27.                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  
  28.                    if ((mPrivateFlags & PRESSED) != 0 || prepressed) {  
  29.                        // take focus if we don't have it already and we should in  
  30.                        // touch mode.  
  31.                        boolean focusTaken = false;  
  32.                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {  
  33.                            focusTaken = requestFocus();  
  34.                        }  
  35.   
  36.                        if (!mHasPerformedLongPress) {  
  37.                            // This is a tap, so remove the longpress check  
  38.                            removeLongPressCallback();  
  39.   
  40.                            // Only perform take click actions if we were in the pressed state  
  41.                            if (!focusTaken) {  
  42.                                // Use a Runnable and post this rather than calling  
  43.                                // performClick directly. This lets other visual state  
  44.                                // of the view update before click actions start.  
  45.                                if (mPerformClick == null) {  
  46.                                    mPerformClick = new PerformClick();  
  47.                                }  
  48.                                if (!post(mPerformClick)) {  
  49.                                    performClick();  
  50.                                }  
  51.                            }  
  52.                        }  
  53.   
  54.                        if (mUnsetPressedState == null) {  
  55.                            mUnsetPressedState = new UnsetPressedState();  
  56.                        }  
  57.   
  58.                        if (prepressed) {  
  59.                            mPrivateFlags |= PRESSED;  
  60.                            refreshDrawableState();  
  61.                            postDelayed(mUnsetPressedState,  
  62.                                    ViewConfiguration.getPressedStateDuration());  
  63.                        } else if (!post(mUnsetPressedState)) {  
  64.                            // If the post failed, unpress right now  
  65.                            mUnsetPressedState.run();  
  66.                        }  
  67.                        removeTapCallback();  
  68.                    }  
  69.                    break;  
  70.   
  71.                case MotionEvent.ACTION_DOWN:  
  72.                    if (mPendingCheckForTap == null) {  
  73.                        mPendingCheckForTap = new CheckForTap();  
  74.                    }  
  75.                    mPrivateFlags |= PREPRESSED;  
  76.                    mHasPerformedLongPress = false;  
  77.                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());  
  78.                    break;  
  79.   
  80.                case MotionEvent.ACTION_CANCEL:  
  81.                    mPrivateFlags &= ~PRESSED;  
  82.                    refreshDrawableState();  
  83.                    removeTapCallback();  
  84.                    break;  
  85.   
  86.                case MotionEvent.ACTION_MOVE:  
  87.                    final int x = (int) event.getX();  
  88.                    final int y = (int) event.getY();  
  89.   
  90.                    // Be lenient about moving outside of buttons  
  91.                    int slop = mTouchSlop;  
  92.                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||  
  93.                            (y < 0 - slop) || (y >= getHeight() + slop)) {  
  94.                        // Outside button  
  95.                        removeTapCallback();  
  96.                        if ((mPrivateFlags & PRESSED) != 0) {  
  97.                            // Remove any future long press/tap checks  
  98.                            removeLongPressCallback();  
  99.   
  100.                            // Need to switch from pressed to not pressed  
  101.                            mPrivateFlags &= ~PRESSED;  
  102.                            refreshDrawableState();  
  103.                        }  
  104.                    }  
  105.                    break;  
  106.            }  
  107.            return true;  
  108.        }  
  109.   
  110.        return false;  
  111.    }  
[java] view plaincopy
 
  1. private final class PerformClick implements Runnable {  
  2.        public void run() {  
  3.            performClick();  
  4.        }  
  5.    }  

[java] view plaincopy
 
  1. /** 
  2.  * Call this view's OnClickListener, if it is defined. 
  3.  * 
  4.  * @return True there was an assigned OnClickListener that was called, false 
  5.  *         otherwise is returned. 
  6.  */  
  7. public boolean performClick() {  
  8.     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
  9.   
  10.     if (mOnClickListener != null) {  
  11.         playSoundEffect(SoundEffectConstants.CLICK);  
  12.        mOnClickListener.onClick(this);  
  13.         return true;  
  14.     }  
  15.   
  16.     return false;  
  17. }  


理解了这些其实就不难想象ANR是如何实现的了。当用户执行操作(比如点击了一个按钮)系统会生成一个Message对象,把用户操作的信息写入Message对象,并把这个Message对象压入MessageQueue队列的头部。系统过一段时间(一般是五秒)后会再来检查,刚刚放入的信息是不是已经被处理了,如果信息还在队列中就表明。处理前面信息的过程当中发生的阻塞,用户的操作没有及时得到响应。系统弹出ANR对话框。

作个总结:

  因为UI线程需要保持一直运行的状态,所以要有一个循环保持这个线程不会死掉,但这个线程又必需阻塞,以减少cpu的消耗。android中的这个循环就是通过Looper实现的。有了这个 Looper,Looper就占据了整个线程,导致所有的方法想在些线程中运行就必需通过这个Looper,所以要有个方法可以进入这个Looper的内部。MessageQueue就担当了这个通道 的角色。Message担当了集合的角色。所有在UI线程中运行的方法都必需通过MessageQueue进入Looper内部,不管 是用户定义的方法还是系统事件包括onCreate(),onStop(),用户点击事件etc..

 
附加 : UI线程中,当MessageQueue中的信息被取空时,该线程被阻塞,等新加入Message后,被唤醒。
阻塞:
 
[java] view plaincopy
 
  1. final Message next() {  
  2.         boolean tryIdle = true;  
  3.   
  4.         while (true) {  
  5.             long now;  
  6.             Object[] idlers = null;  
  7.       
  8.             // Try to retrieve the next message, returning if found.  
  9.             synchronized (this) {  
  10.                 now = SystemClock.uptimeMillis();  
  11.                 Message msg = pullNextLocked(now);  
  12.                 if (msg != null) return msg;  
  13.                 if (tryIdle && mIdleHandlers.size() > 0) {  
  14.                     idlers = mIdleHandlers.toArray();  
  15.                 }  
  16.             }  
  17.       
  18.             // There was no message so we are going to wait...  but first,  
  19.             // if there are any idle handlers let them know.  
  20.             boolean didIdle = false;  
  21.             if (idlers != null) {  
  22.                 for (Object idler : idlers) {  
  23.                     boolean keep = false;  
  24.                     try {  
  25.                         didIdle = true;  
  26.                         keep = ((IdleHandler)idler).queueIdle();  
  27.                     } catch (Throwable t) {  
  28.                         Log.wtf("MessageQueue", "IdleHandler threw exception", t);  
  29.                     }  
  30.   
  31.                     if (!keep) {  
  32.                         synchronized (this) {  
  33.                             mIdleHandlers.remove(idler);  
  34.                         }  
  35.                     }  
  36.                 }  
  37.             }  
  38.               
  39.             // While calling an idle handler, a new message could have been  
  40.             // delivered...  so go back and look again for a pending message.  
  41.             if (didIdle) {  
  42.                 tryIdle = false;  
  43.                 continue;  
  44.             }  
  45.   
  46.             synchronized (this) {  
  47.                 // No messages, nobody to tell about it...  time to wait!  
  48.                 try {  
  49.                     if (mMessages != null) {  
  50.                         if (mMessages.when-now > 0) {  
  51.                             Binder.flushPendingCommands();  
  52.                             this.wait(mMessages.when-now);  
  53.                         }  
  54.                     } else {  
  55.                         Binder.flushPendingCommands();  
  56.                         this.wait();  
  57.                     }  
  58.                 }  
  59.                 catch (InterruptedException e) {  
  60.                 }  
  61.             }  
  62.         }  
  63.     }  

在next()中,在最后的synchronized(this)中,if(mMessages != null){  .....   } else{ ...this.wait();....} ,将UI线程阻塞;
唤醒:
[java] view plaincopy
 
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.         if (msg.when != 0) {  
  3.             throw new AndroidRuntimeException(msg  
  4.                     + " This message is already in use.");  
  5.         }  
  6.         if (msg.target == null && !mQuitAllowed) {  
  7.             throw new RuntimeException("Main thread not allowed to quit");  
  8.         }  
  9.         synchronized (this) {  
  10.             if (mQuiting) {  
  11.                 RuntimeException e = new RuntimeException(  
  12.                     msg.target + " sending message to a Handler on a dead thread");  
  13.                 Log.w("MessageQueue", e.getMessage(), e);  
  14.                 return false;  
  15.             } else if (msg.target == null) {  
  16.                 mQuiting = true;  
  17.             }  
  18.   
  19.             msg.when = when;  
  20.             //Log.d("MessageQueue", "Enqueing: " + msg);  
  21.             Message p = mMessages;  
  22.             if (p == null || when == 0 || when < p.when) {  
  23.                 msg.next = p;  
  24.                 mMessages = msg;  
  25.                 this.notify();  
  26.             } else {  
  27.                 Message prev = null;  
  28.                 while (p != null && p.when <= when) {  
  29.                     prev = p;  
  30.                     p = p.next;  
  31.                 }  
  32.                 msg.next = prev.next;  
  33.                 prev.next = msg;  
  34.                 this.notify();  
  35.             }  
  36.         }  
  37.         return true;  
  38.     }  
在enqueueMessage()中,this.notify(),将UI线程唤醒

在这里有一个问题:当MessageQueue里没有Message后,UI线程wait。 之后又是怎样唤醒的呢?是通过其他线程将Message压入到UI线程的MessageQueue吗?然后再将UI线程notify的吗?那这些线程是哪些呢?debug的时候发现,当屏幕suspend,或者resume;click等事件发生时,UI线程的MessageQueue被压入了Message?这些都是由物理状态改变所引起的,是不是有一个线程专门监视设备的物理状态,当其状态发生改变时,发送Message至UI线程的MessageQueue,以此notify UI线程?一如activity.runOnUiThread()方法类似。这些不大明白,留待以后再深入研究下
 
[java] view plaincopy
 
  1. /** 
  2.  * Runs the specified action on the UI thread. If the current thread is the UI 
  3.  * thread, then the action is executed immediately. If the current thread is 
  4.  * not the UI thread, the action is posted to the event queue of the UI thread. 
  5.  * 
  6.  * @param action the action to run on the UI thread 
  7.  */  
  8. public final void runOnUiThread(Runnable action) {  
  9.     if (Thread.currentThread() != mUiThread) {  
  10.         mHandler.post(action);  
  11.     } else {  
  12.         action.run();  
  13.     }  
  14. }  
又如:
[java] view plaincopy
 
  1. private Button  mButton;  
  2.       
  3.     @Override  
  4.     public void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         mButton = (Button) findViewById(R.id.btn);  
  8.         mButton.setOnClickListener(new Button.OnClickListener(){  
  9.   
  10.             @Override  
  11.             public void onClick(View v) {  
  12.                 // TODO Auto-generated method stub  
  13.                 Log.i("TAG","button.onclick");  
  14.                   
  15.                 Thread thread1 = new Thread(runnable1);  
  16.                 thread1.start();  
  17.                   
  18.                   
  19.             }});  
  20.     }  
  21.   
  22.     private Handler mHandler = new Handler(){  
  23.   
  24.         @Override  
  25.         public void handleMessage(Message msg) {  
  26.             // TODO Auto-generated method stub  
  27.             super.handleMessage(msg);  
  28.         }  
  29.   
  30.         @Override  
  31.         public void dispatchMessage(Message msg) {  
  32.             // TODO Auto-generated method stub  
  33.               
  34.             Thread thread = Thread.currentThread();  
  35.             Log.i("TAG","dispatchMessage();  threadName = " + thread.getName());  
  36.               
  37.             super.dispatchMessage(msg);  
  38.         }  
  39.   
  40.         @Override  
  41.         public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  42.             // TODO Auto-generated method stub  
  43.               
  44.             Thread thread = Thread.currentThread();  
  45.             Log.i("TAG","sendMessageAtTime();  threadName = " + thread.getName());  
  46.               
  47.             return super.sendMessageAtTime(msg, uptimeMillis);  
  48.         }  
  49.           
  50.     };  
  51.       
  52.     private Runnable runnable1 = new Runnable(){  
  53.   
  54.         @Override  
  55.         public void run() {  
  56.               
  57.             Thread thread = Thread.currentThread();  
  58.             Log.i("TAG","runnable1.run(); threadName = " + thread.getName());  
  59.               
  60.             try {  
  61.                 Thread.sleep(3000);  
  62.             } catch (InterruptedException e) {  
  63.                 // TODO Auto-generated catch block  
  64.                 e.printStackTrace();  
  65.             }  
  66.               
  67.             mHandler.sendEmptyMessage(1);  
  68.         }  
  69.           
  70.     };  
 
 
In Handler.class
 
[java] view plaincopy
 
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  2.   {  
  3.       boolean sent = false;  
  4.       MessageQueue queue = mQueue;  
  5.       if (queue != null) {  
  6.           msg.target = this;  
  7.           sent = queue.enqueueMessage(msg, uptimeMillis);  
  8.       }  
  9.       else {  
  10.           RuntimeException e = new RuntimeException(  
  11.               this + " sendMessageAtTime() called with no mQueue");  
  12.           Log.w("Looper", e.getMessage(), e);  
  13.       }  
  14.       return sent;  
  15.   }  



结果:

很显然,在thread-8中,mHandler将msg发送到主线程的Message Queue,主线程被notify,然后取出msg,由mHandler dispatchMessage,然后在主线程 handle msg.

其实在handler中,有一个mLooper,mQueue,其在默认状态下,是对应于定义handler那个的线程的

 

posted @ 2014-12-14 22:34  Apoor_fish  阅读(125)  评论(0)    收藏  举报