Handler消息處理機(jī)制

Handler + Looper + Message + Message Queue關(guān)系

基本概念
  • Message:消息對象
  • MessageQueue:存儲(chǔ)消息對象的隊(duì)列
  • Looper:負(fù)責(zé)循環(huán)讀取MessageQueen中的消息,讀到消息之后就把消息交給Handler去處理。
  • Handler:發(fā)送消息和處理消息
基本方法
相互關(guān)系
詳細(xì)關(guān)系

下面從源碼角度分別看下各個(gè)對象的作用。

Looper

要想使用Handler ,首先要保證當(dāng)前所在線程存在Looper對象。主線程不需要主動(dòng)創(chuàng)建Looper對象是因?yàn)橹骶€程已經(jīng)為你準(zhǔn)備好了,詳見android.app.ActivityThread->Looper.prepareMainLooper()
我們創(chuàng)建的子線程如果想用Handler接收數(shù)據(jù),需要先通過Looper.prepare()創(chuàng)建Looper

 Looper.prepare();
 // 創(chuàng)建Handler并傳入
 Looper.loop()
Looper構(gòu)造方法
private Looper(boolean quitAllowed) {
  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();
}

創(chuàng)建Looper對象的時(shí)候,同時(shí)創(chuàng)建了MessageQueue,并讓Looper綁定當(dāng)前線程。但我們從來不直接調(diào)用構(gòu)造方法獲取Looper對象,而是使用Looperprepare()方法。prepare()使用ThreadLocal保存當(dāng)前Looper對象,ThreadLocal類可以對數(shù)據(jù)進(jìn)行線程隔離,保證了在當(dāng)前線程只能獲取當(dāng)前線程的Looper對象,同時(shí)prepare()保證當(dāng)前線程有且只有一個(gè)Looper對象,間接保證了一個(gè)線程只有一個(gè)MessageQueue對象。
MessageQueue 只是一個(gè)消息的存儲(chǔ)單元,它不能去處理消息,Looper填補(bǔ)了這個(gè)功能,Looper會(huì)以無限循環(huán)的模式去查看Message中是否有新消息,否則就一直等待。

Looper的prepare()
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));
}
Looper開啟循環(huán)
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;
  Binder.clearCallingIdentity();
  final long ident = Binder.clearCallingIdentity();
  for (;;) {
    // might block 也許會(huì)堵塞,一會(huì)在next方法中解析
    Message msg = queue.next();
    if (msg == null) {
      return;
    }
    try {
      msg.target.dispatchMessage(msg);
    } finally {
      if (traceTag != 0) {
        Trace.traceEnd(traceTag);
      }
    }
    msg.recycleUnchecked();
  }
}

Lopper通過loop()開啟無限循環(huán),通過MessageQueuenext()獲取message對象。一旦獲取就調(diào)用msg.target.dispatchMEssage(msg)msg交給Handler對象處理(msg.targetHandler對象),最后回收。

Handler

Hanlder實(shí)例化
public Handler(Callback callback, boolean async) {
  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;
}

實(shí)例化過程中獲取當(dāng)前線程的MessageQueue對象,以便于將消息加入MessageQueue

發(fā)送消息
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);
} 
將消息加入隊(duì)列
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  msg.target = this;
  if (mAsynchronous) {
    msg.setAsynchronous(true);
  }
  return queue.enqueueMessage(msg, uptimeMillis);
}

enqueueMessage()中首先為msg.target賦值為this,為發(fā)送消息出隊(duì)列交給Handler處理埋下伏筆。

處理消息
public void dispatchMessage(Message msg) {
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    if (mCallback != null) {
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    handleMessage(msg);
  }
}

前面我們提到Looper.loop()獲取到消息時(shí)會(huì)調(diào)用HandlerdispatchMessage()方法進(jìn)行處理,Handler處理消息就是調(diào)用我們重寫的handleMessage()方法,或者我們可以在創(chuàng)建Handler實(shí)例時(shí)實(shí)現(xiàn)Callback接口,一樣可以處理從MessageQueue出來的消息.

MessageQueue

MessageQueue 構(gòu)造方法
MessageQueue(boolean quitAllowed) {
  mQuitAllowed = quitAllowed;
  mPtr = nativeInit();
}

MessageQueue初始化過程同時(shí)初始化底層的NativeMessageQueue對象,并且持有NativeMessageQueue的內(nèi)存地址(long)。
MessageQueue它內(nèi)部存儲(chǔ)了一組消息,以隊(duì)列的形式對外提供插入和刪除的工作。內(nèi)部結(jié)構(gòu)不是真正的隊(duì)列,而是采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)消息列表。

MessageQueue的next()
Message next() {
  final long ptr = mPtr;
  if (ptr == 0) {
    return null;
  }
  int pendingIdleHandlerCount = -1; // -1 only during first iteration
  int nextPollTimeoutMillis = 0;
  for (;;) {
    if (nextPollTimeoutMillis != 0) {
      Binder.flushPendingCommands(); // 刷一下,就當(dāng)是Android系統(tǒng)的一種性能優(yōu)化操作
    }
    nativePollOnce(ptr, nextPollTimeoutMillis); // native底層實(shí)現(xiàn)堵塞,堵塞狀態(tài)可被新消息喚醒,頭一次進(jìn)來不會(huì)延遲
    synchronized (this) {
      final long now = SystemClock.uptimeMillis();
      Message prevMsg = null;
      Message msg = mMessages;//獲取頭節(jié)點(diǎn)消息
      if (msg != null && msg.target == null) {
        do {
          prevMsg = msg;
          msg = msg.next;
        } while (msg != null && !msg.isAsynchronous());
      }
      if (msg != null) {
        if (now < msg.when) {
          nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);//獲取堵塞時(shí)間
        } else {
          mBlocked = false;
          if (prevMsg != null) { // 頭結(jié)點(diǎn)指向隊(duì)列中第二個(gè)消息對象
            prevMsg.next = msg.next;
          } else {
            mMessages = msg.next;
          }
          msg.next = null;
          if (DEBUG) {
            Log.v(TAG, "Returning message: " + msg);
          }
          msg.markInUse();
          return msg;   // 直接出隊(duì)列返回給looper
        }
      } else {
        nextPollTimeoutMillis = -1; // 隊(duì)列已無消息,一直堵塞
      }
      if (mQuitting) {
        dispose();
        return null;
      }
      pendingIdleHandlerCount = 0;
      nextPollTimeoutMillis = 0;
    }
  }
}

雖然looper也開啟了循環(huán),但是到了真正干活的時(shí)候它卻調(diào)用了MessageQueuenext(),要想搞明白怎么個(gè)堵塞,先看這三個(gè)對應(yīng)的條件:

  • nextPollTimeoutMillis=0 不堵塞
  • nextPollTimeoutMillis<0 一直堵塞
  • nextPollTimeoutMillis>0 堵塞對應(yīng)時(shí)長,可被新消息喚醒

next()中,因?yàn)橄㈥?duì)列是按照延遲時(shí)間排序的,所以先考慮延遲最小的也就是頭消息。當(dāng)頭消息為空,說明隊(duì)列中沒有消息了,nextPollTimeoutMIllis就被賦值為-1,當(dāng)頭消息延遲時(shí)間大于當(dāng)前時(shí)間,堵塞消息要到延遲時(shí)間和當(dāng)前時(shí)間的差值
當(dāng)消息延遲時(shí)間小于等于0,直接返回msgHandler處理
nativePollOnce(ptr, nextPollTimeoutMillis)方法是native底層實(shí)現(xiàn)堵塞邏輯,堵塞狀態(tài)會(huì)到時(shí)間喚醒,也可被新消息喚醒,一旦喚醒會(huì)重新獲取頭消息,重新評估是否堵塞或者直接返回消息

消息入棧enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
  if (msg.target == null) {
    throw new IllegalArgumentException("Message must have a target.");
  }
  if (msg.isInUse()) {
    throw new IllegalStateException(msg + " This message is already in use.");
  }

  synchronized (this) {
    if (mQuitting) {
      IllegalStateException e = new IllegalStateException(
                msg.target + " sending message to a Handler on a dead thread");
      Log.w(TAG, e.getMessage(), e);
      msg.recycle();
      return false;
    }

    msg.markInUse();
    msg.when = when;
    Message p = mMessages;
    boolean needWake;
    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.next
      prev.next = msg;
    }

    // We can assume mPtr != 0 because mQuitting is false.
    if (needWake) {
        nativeWake(mPtr);
    }
  }
  return true;
}

消息入棧時(shí),首先會(huì)判斷新消息如果是第一個(gè)消息 或者 新消息沒有延遲 或者 新消息延遲時(shí)間小于隊(duì)列第一個(gè)消息的,都會(huì)立刻對這個(gè)消息進(jìn)行處理。只有當(dāng)消息延遲大于隊(duì)列頭消息時(shí),才會(huì)依次遍歷消息隊(duì)列,將消息按延遲時(shí)間插入消息隊(duì)列響應(yīng)位置。

Message

Message 初始化
public static Message obtain() {
  synchronized (sPoolSync) {
    if (sPool != null) {
      Message m = sPool;
      sPool = m.next;
      m.next = null;
      m.flags = 0; // clear in-use flag
      sPoolSize--;
      return m;
    }
  }
  return new Message();
}

建議使用obtain()獲取Message對象,因?yàn)?code>Message維護(hù)著一個(gè)消息池,這個(gè)消息池的數(shù)據(jù)結(jié)構(gòu)是單向鏈表,優(yōu)先從池子里拿數(shù)據(jù),如果池子里沒有再創(chuàng)建對象。如果Message對象已存在,可以使用obtain(msg)方法,最終也會(huì)調(diào)用obtain()。

消息的回收
void recycleUnchecked() {
  flags = FLAG_IN_USE;
  what = 0;
  arg1 = 0;
  arg2 = 0;
  obj = null;
  replyTo = null;
  sendingUid = -1;
  when = 0;
  target = null;
  callback = null;
  data = null;

  synchronized (sPoolSync) {
    if (sPoolSize < MAX_POOL_SIZE) {
      next = sPool;
      sPool = this;
      sPoolSize++;
    }
  }
}

消息的回收不是將Message對象銷毀,而是將Message對象的值恢復(fù)初始值然后放回池子,等待使用

因?yàn)?code>android會(huì)頻繁的使用Message的對象,使用“池”這種機(jī)制可以減少創(chuàng)建對象開辟內(nèi)存的時(shí)間,更加高效的利用內(nèi)存,因此"池"這種機(jī)制被應(yīng)用于頻繁大量使用的類對象的情況,我們常說的“線程池”也是基于同樣的原理。

總結(jié)

  • 要想在當(dāng)前線程使用Handler機(jī)制,首先確保當(dāng)前線程存在Looper
  • Looper.parper()創(chuàng)建一個(gè) 當(dāng)前線程的Looper對象,同時(shí)創(chuàng)建一個(gè)MessageQueue對象
  • 每個(gè)線程只有一個(gè)Looper對象和一個(gè)MessageQueue對象
  • Looper.loop()開始循環(huán),沒有msg情況下進(jìn)入堵塞狀態(tài)(-1)
  • Message對象最好通過Message.obtain()獲得
  • Handler發(fā)送消息進(jìn)入隊(duì)列,如果沒有延遲喚醒堵塞Looper獲得msg,調(diào)用msg.targe.dispachMessage處理消息
  • 關(guān)閉Activity時(shí)如果棧中有未出棧的message,需清除handler.removeMessage(int)
  • 子線程不再使用Handler時(shí),要調(diào)用loop.quit(),loop.quitSafely()
  • 雖然表面上看是Looper循環(huán)隊(duì)列,并將messageHandler,但實(shí)際上是MessageQueuenext()去完成的,MessageQueue同時(shí)還承擔(dān)消息的入隊(duì)列,并對消息按照延遲時(shí)間從小到大進(jìn)行了排序。鑒于MessageQueue如此大的工作量,在Android 2.3版本后,MessageQueuenext()方法的堵塞機(jī)制轉(zhuǎn)移到native層去處理,也就是我們使用的nativePollOnce(ptr, nextPollTimeoutMillis)方法

異常Can't create handler inside thread that has not called Looper.prepare()

示例代碼:

new Thread(new Runnable() {
  @Override
  public void run() {
    timer = new Timer(mTotalTime, TimeSetted.SECOND_TO_MILL);
    timer.start();
    Log.d(TAG,"Countdown start");
  }
}).start();
  • 出現(xiàn)異常:java.lang.RuntimeException: Can't create handler inside thread Thread that has not called Looper.prepare()崩潰
  • 報(bào)錯(cuò)原因:Timer底層采用的是Handler+Message實(shí)現(xiàn),非主線程中沒有開啟Looper,而 Handler對象必須綁定Looper對象需要調(diào)用Looper.prepare()來給線程創(chuàng)建一個(gè)消息循環(huán),調(diào)用Looper.loop()來使消息循環(huán)起作用。
  • 修復(fù)方法
    • 方案一:即在具體邏輯的前后加入Looper.perpare()Looper.loop()方法。
    • 方案二:通過Looper.getMainLooper(),獲得主線程的Looper,將其綁定到此Handler對象上。

修復(fù)代碼:

new Thread(new Runnable() {
  @Override
  public void run() {
    Looper.perpare(); // 增加部分
    timer = new Timer(mTotalTime, TimeSetted.SECOND_TO_MILL);
    timer.start();
    Log.d(TAG,"Countdown start");
    Looper.loop(); // 增加部分
  }
}).start();

結(jié)論:

  • Can't create handler inside thread Thread that has not called Looper.prepare()通常是在子線程中使用沒有綁定Looperhandler時(shí)出現(xiàn),只需在handler語句的前后加Looper.prepare()Looper.loop()方法即可。
  • 創(chuàng)建子線程Thread和使用AsyncTaskdoInBackground時(shí),若沒有new一個(gè)handler對象,通常不會(huì)出現(xiàn)這個(gè)錯(cuò)誤。本人是由于在調(diào)用其他方法時(shí)用了handler導(dǎo)致報(bào)錯(cuò)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容