Android Looper和Handler分析

Android應用程序是經過消息來驅動的,每一個應用程序都有一個Main looper在ActivityThread中建立。咱們這一節中就主要來分析下Looper和Handler的實現機制,首先來簡單介紹一下它們的關係: java

▪Thread、Looper、MessageQueue、Handler的關係
–Thread線程是整個Looper循環執行的場所
–Looper消息泵,不斷的從MessageQueue中讀取消息並執行,Looper就是一個無限循環,Looper中包含MessageQueue
–MessageQueue消息隊列,負責存放消息
–Looper分發消息給Handler執行;Handler同時能夠向MessageQueue添加消息

咱們經過下面一個簡單的程序來看一下如何使用Looper和Handler:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  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.      }  

首先在一個Thread中須要先調用Looper.prepare方法去作好初始化工做,其實就是實例化一個MessageQueue。而後調用Looper.loop就能夠開始循環了。那咱們首先先看一下prepare方法:

[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public static void prepare() {  
  2.     prepare(true);  
  3. }  
  4. private static void prepare(boolean quitAllowed) {  
  5.     if (sThreadLocal.get() != null) {  
  6.         throw new RuntimeException("Only one Looper may be created per thread");  
  7.     }  
  8.     sThreadLocal.set(new Looper(quitAllowed));  
  9. }  
  10.   
  11. private Looper(boolean quitAllowed) {  
  12.     mQueue = new MessageQueue(quitAllowed);  
  13.     mThread = Thread.currentThread();  
  14. }  

能夠看到在prepare方法中主要是構造一個Looper對象並存放在sThreadLocal中,sThreadLocal是線程本地存儲的變量,每一個線程有這麼一塊區域來存儲線程的數據,這些數據不會被進程中其它線程所修改。在Looper的構造函數中實例化一個MessageQueue對象:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1.     MessageQueue(boolean quitAllowed) {  
  2.         mQuitAllowed = quitAllowed;  
  3.         mPtr = nativeInit();  
  4.     }  
  5.   
  6. static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {  
  7.     NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();  
  8.     if (!nativeMessageQueue) {  
  9.         jniThrowRuntimeException(env, "Unable to allocate native queue");  
  10.         return 0;  
  11.     }  
  12.   
  13.     nativeMessageQueue->incStrong(env);  
  14.     return reinterpret_cast<jint>(nativeMessageQueue);  
  15. }  

在MessageQueue的構造函數中經過JNI調用到android_os_MessageQueue_nativeInit方法,在這個方法裏面,構造一個NativeMessageQueue對象,並在Java層的MessageQueue成員變量mPtrl保存NativeMessageQueue對象的內存地址,以便後面Java層調用NativeMessageQueue的其它方法。咱們再來看一下NativeMessageQueue的構造函數:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {  
  2.     mLooper = Looper::getForThread();  
  3.     if (mLooper == NULL) {  
  4.         mLooper = new Looper(false);  
  5.         Looper::setForThread(mLooper);  
  6.     }  
  7. }  
在Native層的MessageQueue中,也經過TLS技術在線程中保存是否建立了底層Looper,若是有建立就能夠經過getForThread返回;若是沒有,getForThread將返回NULL。固然這裏確定會返回NULL,這裏就將構造一個Looper對象並設置到這個線程的TLS中。咱們來看Looper的構造函數:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. Looper::Looper(bool allowNonCallbacks) :  
  2.         mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),  
  3.         mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {  
  4.     int wakeFds[2];  
  5.     int result = pipe(wakeFds);  
  6.     LOG_ALWAYS_FATAL_IF(result != 0"Could not create wake pipe.  errno=%d", errno);  
  7.   
  8.     mWakeReadPipeFd = wakeFds[0];  
  9.     mWakeWritePipeFd = wakeFds[1];  
  10.   
  11.     result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);  
  12.     LOG_ALWAYS_FATAL_IF(result != 0"Could not make wake read pipe non-blocking.  errno=%d",  
  13.             errno);  
  14.   
  15.     result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);  
  16.     LOG_ALWAYS_FATAL_IF(result != 0"Could not make wake write pipe non-blocking.  errno=%d",  
  17.             errno);  
  18.   
  19.     mIdling = false;  
  20.   
  21.     // Allocate the epoll instance and register the wake pipe.  
  22.     mEpollFd = epoll_create(EPOLL_SIZE_HINT);  
  23.     LOG_ALWAYS_FATAL_IF(mEpollFd < 0"Could not create epoll instance.  errno=%d", errno);  
  24.   
  25.     struct epoll_event eventItem;  
  26.     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union  
  27.     eventItem.events = EPOLLIN;  
  28.     eventItem.data.fd = mWakeReadPipeFd;  
  29.     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);  
  30.     LOG_ALWAYS_FATAL_IF(result != 0"Could not add wake read pipe to epoll instance.  errno=%d",  
  31.             errno);  
  32. }  

Looper的構造函數比較簡單,首先構造一個pipe,一端用於讀,另外一端用於寫。而後使用epoll將它mWakeReadPipeFd添加到mEpollFd中。後面咱們就能夠經過在mWakeWritePipeFd端寫數據,讓epoll_wait跳出等待。當這裏Looper.prepare函數就介紹完了,咱們先來看一下上面說到的幾個類的關係圖:





而後咱們再來分析Looper.loop方法:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public static void loop() {  
  2.     final Looper me = myLooper();  
  3.     final MessageQueue queue = me.mQueue;  
  4.     Binder.clearCallingIdentity();  
  5.     final long ident = Binder.clearCallingIdentity();  
  6.     for (;;) {  
  7.         Message msg = queue.next(); // might block  
  8.         if (msg == null) {  
  9.             return;  
  10.         }  
  11.         msg.target.dispatchMessage(msg);  
  12.         final long newIdent = Binder.clearCallingIdentity();  
  13.         msg.recycle();  
  14.     }  
  15. }  
首先myLooper返回ThreadLocal存儲的前面構造的Looper對象。而後調用Looper中的MessageQueue的next方法,next方法返回下一個消息(若是有,若是沒有就一直等待),固然通常狀況下不會返回空消息。並調用msg.target的dispatchMessage方法,這裏的target其實就是Handler,咱們後面再來分析。先來看一下MessageQueue的next方法:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. Message next() {  
  2.      int pendingIdleHandlerCount = -1// -1 only during first iteration  
  3.      int nextPollTimeoutMillis = 0;  
  4.      for (;;) {  
  5.          if (nextPollTimeoutMillis != 0) {  
  6.              Binder.flushPendingCommands();  
  7.          }  
  8.          nativePollOnce(mPtr, nextPollTimeoutMillis);  
  9.          synchronized (this) {  
  10.              final long now = SystemClock.uptimeMillis();  
  11.              Message prevMsg = null;  
  12.              Message msg = mMessages;  
  13.              if (msg != null && msg.target == null) {  
  14.                  do {  
  15.                      prevMsg = msg;  
  16.                      msg = msg.next;  
  17.                  } while (msg != null && !msg.isAsynchronous());  
  18.              }  
  19.              if (msg != null) {  
  20.                  if (now < msg.when) {  
  21.                      nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);  
  22.                  } else {  
  23.                      mBlocked = false;  
  24.                      if (prevMsg != null) {  
  25.                          prevMsg.next = msg.next;  
  26.                      } else {  
  27.                          mMessages = msg.next;  
  28.                      }  
  29.                      msg.next = null;  
  30.                      msg.markInUse();  
  31.                      return msg;  
  32.                  }  
  33.              } else {  
  34.                  nextPollTimeoutMillis = -1;  
  35.              }  
  36.              if (mQuitting) {  
  37.                  dispose();  
  38.                  return null;  
  39.              }  
  40.   
  41.              if (pendingIdleHandlerCount < 0  
  42.                      && (mMessages == null || now < mMessages.when)) {  
  43.                  pendingIdleHandlerCount = mIdleHandlers.size();  
  44.              }  
  45.              if (pendingIdleHandlerCount <= 0) {  
  46.                  // No idle handlers to run.  Loop and wait some more.  
  47.                  mBlocked = true;  
  48.                  continue;  
  49.              }  
  50.              if (mPendingIdleHandlers == null) {  
  51.                  mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];  
  52.              }  
  53.              mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);  
  54.          }  
  55.   
  56.          for (int i = 0; i < pendingIdleHandlerCount; i++) {  
  57.              final IdleHandler idler = mPendingIdleHandlers[i];  
  58.              mPendingIdleHandlers[i] = null// release the reference to the handler  
  59.              boolean keep = false;  
  60.              try {  
  61.                  keep = idler.queueIdle();  
  62.              } catch (Throwable t) {  
  63.                  Log.wtf("MessageQueue""IdleHandler threw exception", t);  
  64.              }  
  65.              if (!keep) {  
  66.                  synchronized (this) {  
  67.                      mIdleHandlers.remove(idler);  
  68.                  }  
  69.              }  
  70.          }  
  71.          pendingIdleHandlerCount = 0;  
  72.          nextPollTimeoutMillis = 0;  
  73.      }  
  74.  }  

next函數雖然比較長,但它的邏輯仍是比較簡單的,主要能夠分爲下面三個步驟:
▪調用nativePollOnce去完成等待。初始值nextPollTimeoutMillis爲0,epoll_wait會立刻返回,當nextPollTimeoutMillis爲-1,epoll_wait會一直等待
▪當nativePollOnce返回後,獲取mMessages中消息。若是mMessages沒有消息,就設置nextPollTimeoutMillis爲-1,表示下一次epoll_wait時一直等待。若是mMessages中有消息,而且當前系統時間不小於messge待處理的時間,就返回這個消息
▪若是沒有消息處理,而且當前有IdleHandlers,就調用IdleHandlers的queueIdle方法,並修改nextPollTimeoutMillis爲0。IdleHandlers用於在MessageQueue中沒有消息時作回調使用。

在上面的三個步驟中,最重要的固然是nativePollOnce,咱們來簡單分析一下:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {  
  2.     mInCallback = true;  
  3.     mLooper->pollOnce(timeoutMillis);  
  4.     mInCallback = false;  
  5.     if (mExceptionObj) {  
  6.         env->Throw(mExceptionObj);  
  7.         env->DeleteLocalRef(mExceptionObj);  
  8.         mExceptionObj = NULL;  
  9.     }  
  10. }  
  11.   
  12. int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {  
  13.     int result = 0;  
  14.     for (;;) {  
  15.   
  16.   
  17.         if (result != 0) {  
  18.             return result;  
  19.         }  
  20.   
  21.         result = pollInner(timeoutMillis);  
  22.     }  
  23. }  
  24.   
  25. int Looper::pollInner(int timeoutMillis) {  
  26.     int result = ALOOPER_POLL_WAKE;  
  27.     mResponses.clear();  
  28.     mResponseIndex = 0;  
  29.   
  30.     // We are about to idle.  
  31.     mIdling = true;  
  32.   
  33.     struct epoll_event eventItems[EPOLL_MAX_EVENTS];  
  34.     int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);  
  35.   
  36.     // No longer idling.  
  37.     mIdling = false;  
  38.   
  39.     // Acquire lock.  
  40.     mLock.lock();  
  41.   
  42.     if (eventCount < 0) {  
  43.         if (errno == EINTR) {  
  44.             goto Done;  
  45.         }  
  46.         ALOGW("Poll failed with an unexpected error, errno=%d", errno);  
  47.         result = ALOOPER_POLL_ERROR;  
  48.         goto Done;  
  49.     }  
  50.   
  51.     // Check for poll timeout.  
  52.     if (eventCount == 0) {  
  53. #if DEBUG_POLL_AND_WAKE  
  54.         ALOGD("%p ~ pollOnce - timeout"this);  
  55. #endif  
  56.         result = ALOOPER_POLL_TIMEOUT;  
  57.         goto Done;  
  58.     }  
  59.   
  60.     for (int i = 0; i < eventCount; i++) {  
  61.         int fd = eventItems[i].data.fd;  
  62.         uint32_t epollEvents = eventItems[i].events;  
  63.         if (fd == mWakeReadPipeFd) {  
  64.             if (epollEvents & EPOLLIN) {  
  65.                 awoken();  
  66.             } else {  
  67.             }  
  68.         } else {  
  69.   
  70.         }  
  71.     }  
  72. Done: ;  
  73.   
  74.     mNextMessageUptime = LLONG_MAX;  
  75.     mLock.unlock();  
  76.     return result;  
  77. }  

由於Native層Looper須要支持底層本身的消息處理機制,因此pollOnce的代碼中添加處理底層Message的代碼,咱們拋開這部分的代碼,其實pollOnce就是調用epoll_wait去等待時間發生。當timeoutMillis爲0時,它會當即返回;當timeoutMillis爲-1時,它會一直等待,知道咱們調用Looper::wake方法向mWakeWritePipeFd寫入數據。咱們簡要來看一下上面介紹prepare和loop的流程:



咱們再來分析下Handler和Message的關係,並介紹如何向MessageQueue中添加消息,以便epoll_wait可以返回。首先來看Handler的構造函數,Handler有不少構造函數,咱們能夠把Handler綁定到一個Looper上,也能夠不帶Looper參數,它會默認的綁定到咱們的MainThread中:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public Handler(Callback callback) {  
  2.     this(callback, false);  
  3. }  
  4.   
  5. public Handler(Looper looper) {  
  6.     this(looper, nullfalse);  
  7. }  
  8.   
  9. public Handler(Callback callback, boolean async) {  
  10.     mLooper = Looper.myLooper();  
  11.     if (mLooper == null) {  
  12.         throw new RuntimeException(  
  13.             "Can't create handler inside thread that has not called Looper.prepare()");  
  14.     }  
  15.     mQueue = mLooper.mQueue;  
  16.     mCallback = callback;  
  17.     mAsynchronous = async;  
  18. }  
  19.   
  20. public Handler(Looper looper, Callback callback, boolean async) {  
  21.     mLooper = looper;  
  22.     mQueue = looper.mQueue;  
  23.     mCallback = callback;  
  24.     mAsynchronous = async;  
  25. }  

上面列舉了兩種Handler的構造方法,它主要從當前Looper中獲得MessageQueue對象,並保存在mQueue中,後面咱們就能夠調用mQueue的方法來添加消息了。來看一下Handler和Message的類圖:



來看一下一個簡單的sendMessage方法,固然Message對象能夠經過Message的靜態方法obtain得到:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public final boolean sendMessage(Message msg)  
  2. {  
  3.     return sendMessageDelayed(msg, 0);  
  4. }  
  5.   
  6. public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  7. {  
  8.     if (delayMillis < 0) {  
  9.         delayMillis = 0;  
  10.     }  
  11.     return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  12. }  
  13.   
  14. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  15.     MessageQueue queue = mQueue;  
  16.     return enqueueMessage(queue, msg, uptimeMillis);  
  17. }  
  18.   
  19. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  20.     msg.target = this;  
  21.     if (mAsynchronous) {  
  22.         msg.setAsynchronous(true);  
  23.     }  
  24.     return queue.enqueueMessage(msg, uptimeMillis);  
  25. }  

這裏通過一系列的方法,最終調用到MessageQueue的enqueueMessage函數:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. boolean enqueueMessage(Message msg, long when) {  
  2.     if (msg.target == null) {  
  3.         throw new AndroidRuntimeException("Message must have a target.");  
  4.     }  
  5.     synchronized (this) {  
  6.         msg.when = when;  
  7.         Message p = mMessages;  
  8.         boolean needWake;  
  9.         if (p == null || when == 0 || when < p.when) {  
  10.             msg.next = p;  
  11.             mMessages = msg;  
  12.             needWake = mBlocked;  
  13.         } else {  
  14.             needWake = mBlocked && p.target == null && msg.isAsynchronous();  
  15.             Message prev;  
  16.             for (;;) {  
  17.                 prev = p;  
  18.                 p = p.next;  
  19.                 if (p == null || when < p.when) {  
  20.                     break;  
  21.                 }  
  22.                 if (needWake && p.isAsynchronous()) {  
  23.                     needWake = false;  
  24.                 }  
  25.             }  
  26.             msg.next = p; // invariant: p == prev.next  
  27.             prev.next = msg;  
  28.         }  
  29.         // We can assume mPtr != 0 because mQuitting is false.  
  30.         if (needWake) {  
  31.             nativeWake(mPtr);  
  32.         }  
  33.     }  
  34.     return true;  
  35. }  


enqueueMessage檢查mMessages中是否有消息,若是沒有,就把它當前頭添加到mMessages中,並更新needWake爲mBlocked,mBlocked會在mMessages爲空而且沒有IdleHandlers時置爲true,這時timeoutMillis爲-1,epoll_wait會無限等待,因此咱們須要調用natvieWake喚醒它;若是在mMessages有消息,咱們通常狀況下不須要調用nativeWake來喚醒,除非咱們當前頭部是barrier消息(target爲NULL)而且待send的消息是第一個異步的,這裏就將調用nativeWake來喚醒它。這裏注意的是異步消息不會被barrier消息打斷,而且異步消息能夠在它以前的同步消息以前執行。再來看一下nativeWake函數的實現:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) {  
  2.     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);  
  3.     return nativeMessageQueue->wake();  
  4. }  
  5.   
  6. void Looper::wake() {  
  7.     ssize_t nWrite;  
  8.     do {  
  9.         nWrite = write(mWakeWritePipeFd, "W"1);  
  10.     } while (nWrite == -1 && errno == EINTR);  
  11.   
  12.     if (nWrite != 1) {  
  13.         if (errno != EAGAIN) {  
  14.             ALOGW("Could not write wake signal, errno=%d", errno);  
  15.         }  
  16.     }  
  17. }  

這裏其實就是向pipe的寫端寫入一個"W"字符,這樣epoll_wait就能夠跳出等待了。咱們先來看一下上面介紹的sendMessage的流程:



當Message的next方法返回一個消息後,後面就將調用Handler的dispatchMessage去處理它:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public void dispatchMessage(Message msg) {  
  2.     if (msg.callback != null) {  
  3.         handleCallback(msg);  
  4.     } else {  
  5.         if (mCallback != null) {  
  6.             if (mCallback.handleMessage(msg)) {  
  7.                 return;  
  8.             }  
  9.         }  
  10.         handleMessage(msg);  
  11.     }  
  12. }  

當咱們post一個Runnable時,Message的callback就爲這個Runnable,若是Runnable不爲空,直接調用callback.run方法。若是msg.callback爲空,但mCallback不爲空,則調用mCallback的handleMessage方法。最後二者都沒有的狀況下才調用Handler的handleMessage方法,因此咱們在程序中通常重載handleMessage來處理消息便可。下面是dispatchMessage的流程圖:




咱們來看下面這段代碼:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. class LooperThread extends Thread {  
  2.     public Looper myLooper = null;  
  3.     public void run() {  
  4.         Looper.prepare();  
  5.         myLooper = Looper.myLooper();  
  6.         Looper.loop();  
  7.         }  
  8. }  
  9.   
  10. {  
  11.     LooperThread myLooperThread = new LooperThread();  
  12.     myLooperThread.start();  
  13.     Looper mLooper = myLooperThread.myLooper;  
  14.     Handler mHandler = new Handler(mLooper);  
  15.     mHandler.sendEmptyMessage(0);  
  16. }  

這在咱們程序中會常常用到,先在一個Thread中準備好Looper,而後使用這個Looper綁定到另一個Handler上面。但上面的程序有點bug,當myLooperThread還沒執行到myLooper = Looper.myLooper()這一行時,主線程接着運行並調用Handler的構造函數,由於此時MessageQueue還沒準備好,因此這裏會拋出一個異常。爲了處理這種問題,Android提供了HandlerThread這個類來完美解決這個問題:
[java]  view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. public HandlerThread(String name, int priority) {  
  2.     super(name);  
  3.     mPriority = priority;  
  4. }  
  5.   
  6. public void run() {  
  7.     mTid = Process.myTid();  
  8.     Looper.prepare();  
  9.     synchronized (this) {  
  10.         mLooper = Looper.myLooper();  
  11.         notifyAll();  
  12.     }  
  13.     Process.setThreadPriority(mPriority);  
  14.     onLooperPrepared();  
  15.     Looper.loop();  
  16.     mTid = -1;  
  17. }  
  18.   
  19. public Looper getLooper() {  
  20.     if (!isAlive()) {  
  21.         return null;  
  22.     }  
  23.       
  24.     // If the thread has been started, wait until the looper has been created.  
  25.     synchronized (this) {  
  26.         while (isAlive() && mLooper == null) {  
  27.             try {  
  28.                 wait();  
  29.             } catch (InterruptedException e) {  
  30.             }  
  31.         }  
  32.     }  
  33.     return mLooper;  
  34. }  

經過wait和notifyAll機制完美解決了以上的問題。看來咱們在程序中仍是要多使用HandlerThread。下面對上面的介紹作一個簡單的總結:
▪Handler的處理過程運行在建立Handler的線程裏 ▪一個Looper對應一個MessageQueue ▪一個線程對應一個Looper ▪一個Looper能夠對應多個Handler ▪當MessageQueue中沒有消息時,IdleHandlers會被回調 ▪MessageQueue中的消息原本是有序的處理,但能夠經過barrier消息將其中斷(打亂)
相關文章
相關標籤/搜索