分享

Android消息处理探秘 .

 aaie_ 2012-09-05

下图为Android消息处理机制顺序图:


由图可知,第一步通过调用Looper.prepare()来创建Looper和MessageQueue java对象,MessageQueue java类通过保存单向链表头mMessages来遍历所有消息,注意此单向链表按时间顺序从早到晚排列,因此mMessages指向的消息总是为需最早处理的消息。在第5步创建C++ Looper对象,在其中创建读写管道并通过epoll来监控读管道。

第7步创建Handler给用户使用,第8步在消息处理线程内调用Looper.loop()来进入消息处理循环,在10步取下一消息时调用nativePollOnce方法,直到13步调到Looper.pollOnce(),在这里将等待管道POLLIN事件一定时间(timeoutMillis毫秒),如果有事件则返回否则等待timeoutMillis毫秒。在第10步MessageQueue.next()取下一消息时,将无限循环直到得到消息。循环时第一次传的超时时间timeoutMillis=0,如果此时尚无消息,则检查是否有IdleHandler,如有则调用所有Handler并置Handler为空,timeoutMillis设为0进行第二次查询, 以后timeoutMillis将设为-1,因此线程将一直挂起,直到有消息到来。

  1. public class MessageQueue{  
  2.     final Message next() {  
  3.         int pendingIdleHandlerCount = -1// -1 only during first iteration   
  4.         int nextPollTimeoutMillis = 0//第一次立刻返回不等待   
  5.   
  6.         for (;;) {  
  7.             if (nextPollTimeoutMillis != 0) {  
  8.                 Binder.flushPendingCommands();  
  9.             }  
  10.             nativePollOnce(mPtr, nextPollTimeoutMillis); //第一次=0   
  11.   
  12.             synchronized (this) {  
  13.                 // Try to retrieve the next message.  Return if found.   
  14.                 final long now = SystemClock.uptimeMillis();  
  15.                 final Message msg = mMessages;  
  16.                 if (msg != null) {  
  17.                     final long when = msg.when;  
  18.                     if (now >= when) { //如果消息发送时间满足   
  19.                         mBlocked = false;  
  20.                         mMessages = msg.next;  
  21.                         msg.next = null;  
  22.                         if (false) Log.v("MessageQueue""Returning message: " + msg);  
  23.                         msg.markInUse();  
  24.                         return msg;     //取消息成功   
  25.                     } else {  
  26.                         nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); //如果有消息但消息尚未到达发送时间,则以该时间到当前时间的时间差作为下次查询超时的时间   
  27.                     }  
  28.                 } else {  
  29.                     nextPollTimeoutMillis = -1;      //无限等待   
  30.                 }  
  31.   
  32.                 // If first time, then get the number of idlers to run.   
  33.                 if (pendingIdleHandlerCount < 0) {  
  34.                     pendingIdleHandlerCount = mIdleHandlers.size();  
  35.                 }  
  36.                 if (pendingIdleHandlerCount == 0) { //如果尚未设置IdleHandler   
  37.                     // No idle handlers to run.  Loop and wait some more.   
  38.                     mBlocked = true;  
  39.                     continue;  
  40.                 }  
  41.   
  42.                 if (mPendingIdleHandlers == null) {  
  43.                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  44.                 }  
  45.                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  46.             }  
  47.   
  48.             // Run the idle handlers.   
  49.             // We only ever reach this code block during the first iteration.   
  50.             for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  51.                 final IdleHandler idler = mPendingIdleHandlers[i];  
  52.                 mPendingIdleHandlers[i] = null// release the reference to the handler   
  53.   
  54.                 boolean keep = false;  
  55.                 try {  
  56.                     keep = idler.queueIdle();  
  57.                 } catch (Throwable t) {  
  58.                     Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  59.                 }  
  60.   
  61.                 if (!keep) {  
  62.                     synchronized (this) {  
  63.                         mIdleHandlers.remove(idler);  
  64.                     }  
  65.                 }  
  66.             }  
  67.   
  68.             // Reset the idle handler count to 0 so we do not run them again.   
  69.             pendingIdleHandlerCount = 0;  
  70.   
  71.             // While calling an idle handler, a new message could have been delivered   
  72.             // so go back and look again for a pending message without waiting.   
  73.             nextPollTimeoutMillis = 0;  
  74.         }  
  75.     }  


第17步用户线程通过Handler发送消息,在20步将调用nativeWake来唤醒消息处理线程。最终第24步消息被处理。


  1. final boolean enqueueMessage(Message msg, long when) {  
  2.     if (msg.isInUse()) {  
  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.     final boolean needWake;  
  10.     synchronized (this) {  
  11.         if (mQuiting) {  
  12.             RuntimeException e = new RuntimeException(  
  13.                 msg.target + " sending message to a Handler on a dead thread");  
  14.             return false;  
  15.         } else if (msg.target == null) {  
  16.             mQuiting = true;  
  17.         }  
  18.   
  19.         msg.when = when;  
  20.         Message p = mMessages;  
  21.         if (p == null || when == 0 || when < p.when) { //如果消息队列为空或者当前消息为发送时间最早的消息   
  22.             msg.next = p;  
  23.             mMessages = msg;  
  24.             needWake = mBlocked; // mBlocked=true表示线程已被挂起   
  25.         } else {  
  26.             Message prev = null;  
  27.             while (p != null && p.when <= when) {  
  28.                 prev = p;  
  29.                 p = p.next;  
  30.             }  
  31.             msg.next = prev.next;  
  32.             prev.next = msg;  
  33.             needWake = false// still waiting on head, no need to wake up   
  34.         }  
  35.     }  
  36.     if (needWake) {  
  37.         nativeWake(mPtr);        //唤醒线程   
  38.     }  
  39.     return true;  
  40. }  


MessageQueue java类单向链表:

  1. public final class Message implements Parcelable {  
  2.     Message next; //消息链表头  


典型的消息处理代码

  1. *  class LooperThread extends Thread {  
  2. *      public Handler mHandler;  
  3. *  
  4. *      public void run() {  
  5. *          Looper.prepare();      //创建Looper对象,在Looper对象创建时将创建MessageQueue.最后将Looper对象存储在线程局部变量中   
  6. *  
  7. *          mHandler = new Handler() { //创建Handler对象,在此将读取线程局部变量得到本线程的Looper对象及消息队列   
  8. *              public void handleMessage(Message msg) {  
  9. *                  // process incoming messages here   
  10. *              }  
  11. *          };  
  12. *  
  13. *          Looper.loop(); //进入Looper对象的消息处理循环   
  14. *      }  
  15. *  }  


用户主要操作接口类Handler,可以通过重载handleMessage()方法实现自定义方法处理:


Handler.java

  1.     public void dispatchMessage(Message msg) {  
  2.         if (msg.callback != null) { //如果消息已设置callback,则调用该callback函数   
  3.             handleCallback(msg);  
  4.         } else {  
  5.             if (mCallback != null) { //如果Handler已设置callback,则调用该callback处理消息   
  6.                 if (mCallback.handleMessage(msg)) { //如果消息被处理   
  7.                     return;  
  8.                 }  
  9.             }  
  10.             handleMessage(msg); //默认为空函数,用户可重载处理自定义消息   
  11.         }  
  12.     }  
  13.     private final void handleCallback(Message message) {  
  14.         message.callback.run();  
  15.     }  
  16.     public Handler(Callback callback) {  
  17.         mLooper = Looper.myLooper(); //得到本线程的Looper对象   
  18.         if (mLooper == null) {  
  19.             throw new RuntimeException(  
  20.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  21.         }  
  22.         mQueue = mLooper.mQueue;  
  23.         mCallback = callback;  
  24.     }  

用户主要操作接口类Looper

frameworks/base/core/java/android/os/Looper.java

  1. public class Looper{  
  2.      /** Initialize the current thread as a looper. 
  3.       * This gives you a chance to create handlers that then reference 
  4.       * this looper, before actually starting the loop. Be sure to call 
  5.       * {@link #loop()} after calling this method, and end it by calling 
  6.       * {@link #quit()}. 
  7.       */  
  8.     public static void prepare() {  
  9.         if (sThreadLocal.get() != null) {  
  10.             throw new RuntimeException("Only one Looper may be created per thread");  
  11.         }  
  12.         sThreadLocal.set(new Looper());  
  13.     }  
  14.   
  15.     /** 
  16.      * Initialize the current thread as a looper, marking it as an 
  17.      * application's main looper. The main looper for your application 
  18.      * is created by the Android environment, so you should never need 
  19.      * to call this function yourself.  See also: {@link #prepare()} 
  20.      */  
  21.     public static void prepareMainLooper() {  
  22.         prepare();  
  23.         setMainLooper(myLooper());  
  24.         myLooper().mQueue.mQuitAllowed = false;  
  25.     }  
  26.   
  27.     public static void loop() {  
  28.         Looper me = myLooper();  
  29.         if (me == null) {  
  30.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  31.         }  
  32.         MessageQueue queue = me.mQueue;  
  33.                   
  34.         while (true) {  
  35.             Message msg = queue.next(); // might block   
  36.             if (msg != null) {  
  37.                 if (msg.target == null) {  
  38.                     // No target is a magic identifier for the quit message.   
  39.                     return;  
  40.                 }  
  41.   
  42.                 long wallStart = 0;  
  43.                 long threadStart = 0;  
  44.   
  45.                 msg.target.dispatchMessage(msg); //处理消息   
  46.                 }                  
  47.                 msg.recycle();  
  48.             }  
  49.         }  
  50.     }  

问题:

1.为了实现消息队列等待机制,采用epoll方式来实现来消息时消息队列处理线程的唤醒和当无消息时线程挂起。实际上如果采用java对象wait/notify来实现此种功能更为简单,难道还有别的因素导致Google采用目前的设计?

分析:估计与IdleHandler有关,目前当消息队列首次为空或取完所有消息后,如果注册了IdleHandler则会调用这些Handler处理,然后才会无限等待下一个消息到来

2.跨进程消息传送如何实现:如鼠标键盘事件传递

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多