Android消息循環(huán)機(jī)制源碼深入理解

Android消息循環(huán)機(jī)制源碼

公司主營業(yè)務(wù):成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)公司推出通河免費做網(wǎng)站回饋大家。

前言:

搞Android的不懂Handler消息循環(huán)機(jī)制,都不好意思說自己是Android工程師。面試的時候一般也都會問這個知識點,但是我相信大多數(shù)碼農(nóng)肯定是沒有看過相關(guān)源碼的,頂多也就是網(wǎng)上搜搜,看看別人的文章介紹。學(xué)姐不想把那個萬能的關(guān)系圖拿出來討論。

近來找了一些關(guān)于android線程間通信的資料,整理學(xué)習(xí)了一下,并制作了一個簡單的例子。

andriod提供了 Handler 和 Looper 來滿足線程間的通信。例如一個子線程從網(wǎng)絡(luò)上下載了一副圖片,當(dāng)它下載完成后會發(fā)送消息給主線程,這個消息是通過綁定在主線程的Handler來傳遞的。

在Android,這里的線程分為有消息循環(huán)的線程和沒有消息循環(huán)的線程,有消息循環(huán)的線程一般都會有一個Looper,這個事android的新 概念。我們的主線程(UI線程)就是一個消息循環(huán)的線程。針對這種消息循環(huán)的機(jī)制,我們引入一個新的機(jī)制Handle,我們有消息循環(huán),就要往消息循環(huán)里 面發(fā)送相應(yīng)的消息,自定義消息一般都會有自己對應(yīng)的處理,消息的發(fā)送和清除,消息的的處理,把這些都封裝在Handle里面,注意Handle只是針對那 些有Looper的線程,不管是UI線程還是子線程,只要你有Looper,我就可以往你的消息隊列里面添加?xùn)|西,并做相應(yīng)的處理。
但是這里還有一點,就是只要是關(guān)于UI相關(guān)的東西,就不能放在子線程中,因為子線程是不能操作UI的,只能進(jìn)行數(shù)據(jù)、系統(tǒng)等其他非UI的操作。

在Android,這里的線程分為有消息循環(huán)的線程和沒有消息循環(huán)的線程,有消息循環(huán)的線程一般都會有一個Looper,這個是android的新概念。我們的主線程(UI線程)就是一個消息循環(huán)的線程。針對這種消息循環(huán)的機(jī)制,我們引入一個新的機(jī)制Handler,我們有消息循環(huán),就要往消息循環(huán)里面發(fā)送相應(yīng)的消息,自定義消息一般都會有自己對應(yīng)的處理,消息的發(fā)送和清除,把這些都封裝在Handler里面,注意Handler只是針對那 些有Looper的線程,不管是UI線程還是子線程,只要你有Looper,我就可以往你的消息隊列里面添加?xùn)|西,并做相應(yīng)的處理。

但是這里還有一點,就是只要是關(guān)于UI相關(guān)的東西,就不能放在子線程中,因為子線程是不能操作UI的,只能進(jìn)行數(shù)據(jù)、系統(tǒng)等其他非UI的操作。

先從我們平時的使用方法引出這個機(jī)制,再結(jié)合源碼進(jìn)行分析。

我們平時使用是這樣的:

 //1. 主線程
 Handler handler = new MyHandler();

 //2. 非主線程
 HandlerThread handlerThread = new HandlerThread("handlerThread");
 handlerThread.start();
 Handler handler = new Handler(handlerThread.getLooper());

 //發(fā)送消息
 handler.sendMessage(msg);

 //接收消息
 static class MyHandler extends Handler {
  //對于非主線程處理消息需要傳Looper,主線程有默認(rèn)的sMainLooper
  public MyHandler(Looper looper) {
   super(looper);
  }

  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
  }
 }

那么為什么初始化的時候,我們執(zhí)行了1或2,后面只需要sendMessage就可處理任務(wù)了呢?學(xué)姐這里以非主線程為例進(jìn)行介紹,handlerThread.start()的時候,實際上創(chuàng)建了一個用于消息循環(huán)的Looper和消息隊列MessageQueue,同時啟動了消息循環(huán),并將這個循環(huán)傳給Handler,這個循環(huán)會從MessageQueue中依次取任務(wù)出來執(zhí)行。用戶若要執(zhí)行某項任務(wù),只需要調(diào)用handler.sendMessage即可,這里做的事情是將消息添加到MessaeQueue中。對于主線程也類似,只是主線程sMainThread和sMainLooper不需要我們主動去創(chuàng)建,程序啟動的時候Application就創(chuàng)建好了,我們只需要創(chuàng)建Handler即可。

我們這里提到了幾個概念:

  • HandlerThread 支持消息循環(huán)的線程
  • Handler 消息處理器
  • Looper 消息循環(huán)對象
  • MessageQueue 消息隊列
  • Message 消息體

對應(yīng)關(guān)系是:一對多,即(一個)HandlerThread、Looper、MessageQueue -> (多個)Handler、Message

源碼解析

1. Looper

(1)創(chuàng)建消息循環(huán)

prepare()用于創(chuàng)建Looper消息循環(huán)對象。Looper對象通過一個成員變量ThreadLocal進(jìn)行保存。

(2)獲取消息循環(huán)對象

myLooper()用于獲取當(dāng)前消息循環(huán)對象。Looper對象從成員變量ThreadLocal中獲取。

(3)開始消息循環(huán)

loop()開始消息循環(huán)。循環(huán)過程如下:

每次從消息隊列MessageQueue中取出一個Message

使用Message對應(yīng)的Handler處理Message

已處理的Message加到本地消息池,循環(huán)復(fù)用

循環(huán)以上步驟,若沒有消息表明消息隊列停止,退出循環(huán)

public static void prepare() {
 prepare(true);
}

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));
}

public static Looper myLooper() {
 return sThreadLocal.get();
}

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;

 // Make sure the identity of this thread is that of the local process,
 // and keep track of what that identity token actually is.
 Binder.clearCallingIdentity();
 final long ident = Binder.clearCallingIdentity();

 for (;;) {
  Message msg = queue.next(); // might block
  if (msg == null) {
   // No message indicates that the message queue is quitting.
   return;
  }

  // This must be in a local variable, in case a UI event sets the logger
  Printer logging = me.mLogging;
  if (logging != null) {
   logging.println(">>>>> Dispatching to " + msg.target + " " +
     msg.callback + ": " + msg.what);
  }

  msg.target.dispatchMessage(msg);

  if (logging != null) {
   logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  }

  // Make sure that during the course of dispatching the
  // identity of the thread wasn't corrupted.
  final long newIdent = Binder.clearCallingIdentity();
  if (ident != newIdent) {
   Log.wtf(TAG, "Thread identity changed from 0x"
     + Long.toHexString(ident) + " to 0x"
     + Long.toHexString(newIdent) + " while dispatching to "
     + msg.target.getClass().getName() + " "
     + msg.callback + " what=" + msg.what);
  }

  msg.recycleUnchecked();
 }
}

2. Handler

(1)發(fā)送消息

Handler支持2種消息類型,即Runnable和Message。因此發(fā)送消息提供了post(Runnable r)和sendMessage(Message msg)兩個方法。從下面源碼可以看出Runnable賦值給了Message的callback,最終也是封裝成Message對象對象。學(xué)姐個人認(rèn)為外部調(diào)用不統(tǒng)一使用Message,應(yīng)該是兼容Java的線程任務(wù),學(xué)姐認(rèn)為這種思想也可以借鑒到平常開發(fā)過程中。發(fā)送的消息都會入隊到MessageQueue隊列中。

(2)處理消息

Looper循環(huán)過程的時候,是通過dispatchMessage(Message msg)對消息進(jìn)行處理。處理過程:先看是否是Runnable對象,如果是則調(diào)用handleCallback(msg)進(jìn)行處理,最終調(diào)到Runnable.run()方法執(zhí)行線程;如果不是Runnable對象,再看外部是否傳入了Callback處理機(jī)制,若有則使用外部Callback進(jìn)行處理;若既不是Runnable對象也沒有外部Callback,則調(diào)用handleMessage(msg),這個也是我們開發(fā)過程中最常覆寫的方法了。

(3)移除消息

removeCallbacksAndMessages(),移除消息其實也是從MessageQueue中將Message對象移除掉。

public void handleMessage(Message msg) {
}

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

private static void handleCallback(Message message) {
 message.callback.run();
}

public final Message obtainMessage()
{
 return Message.obtain(this);
}

public final boolean post(Runnable r)
{
 return sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean sendMessage(Message msg)
{
 return sendMessageDelayed(msg, 0);
}

private static Message getPostMessage(Runnable r) {
 Message m = Message.obtain();
 m.callback = r;
 return m;
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
 if (delayMillis < 0) {
  delayMillis = 0;
 }
 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
 msg.target = this;
 if (mAsynchronous) {
  msg.setAsynchronous(true);
 }
 return queue.enqueueMessage(msg, uptimeMillis);
}

public final void removeCallbacksAndMessages(Object token) {
 mQueue.removeCallbacksAndMessages(this, token);
}

3. MessageQueue

(1)消息入隊

消息入隊方法enqueueMessage(Message msg, long when)。其處理過程如下:

待入隊的Message標(biāo)記為InUse,when賦值

若消息鏈表mMessages為空為空,或待入隊Message執(zhí)行時間小于mMessage鏈表頭,則待入隊Message添加到鏈表頭

若不符合以上條件,則輪詢鏈表,根據(jù)when從低到高的順序,插入鏈表合適位置

(2)消息輪詢

next()依次從MessageQueue中取出Message

(3)移除消息

removeMessages()可以移除消息,做的事情實際上就是將消息從鏈表移除,同時將移除的消息添加到消息池,提供循環(huán)復(fù)用。

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("MessageQueue", 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;
}

Message next() {
 // Return here if the message loop has already quit and been disposed.
 // This can happen if the application tries to restart a looper after quit
 // which is not supported.
 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();
  }

  nativePollOnce(ptr, nextPollTimeoutMillis);

  synchronized (this) {
   // Try to retrieve the next message. Return if found.
   final long now = SystemClock.uptimeMillis();
   Message prevMsg = null;
   Message msg = mMessages;
   if (msg != null && msg.target == null) {
    // Stalled by a barrier. Find the next asynchronous message in the queue.
    do {
     prevMsg = msg;
     msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
   }
   if (msg != null) {
    if (now < msg.when) {
     // Next message is not ready. Set a timeout to wake up when it is ready.
     nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
    } else {
     // Got a message.
     mBlocked = false;
     if (prevMsg != null) {
      prevMsg.next = msg.next;
     } else {
      mMessages = msg.next;
     }
     msg.next = null;
     if (false) Log.v("MessageQueue", "Returning message: " + msg);
     return msg;
    }
   } else {
    // No more messages.
    nextPollTimeoutMillis = -1;
   }

   // Process the quit message now that all pending messages have been handled.
   if (mQuitting) {
    dispose();
    return null;
   }

   // If first time idle, then get the number of idlers to run.
   // Idle handles only run if the queue is empty or if the first message
   // in the queue (possibly a barrier) is due to be handled in the future.
   if (pendingIdleHandlerCount < 0
     && (mMessages == null || now < mMessages.when)) {
    pendingIdleHandlerCount = mIdleHandlers.size();
   }
   if (pendingIdleHandlerCount <= 0) {
    // No idle handlers to run. Loop and wait some more.
    mBlocked = true;
    continue;
   }

   if (mPendingIdleHandlers == null) {
    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
   }
   mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
  }

  // Run the idle handlers.
  // We only ever reach this code block during the first iteration.
  for (int i = 0; i < pendingIdleHandlerCount; i++) {
   final IdleHandler idler = mPendingIdleHandlers[i];
   mPendingIdleHandlers[i] = null; // release the reference to the handler

   boolean keep = false;
   try {
    keep = idler.queueIdle();
   } catch (Throwable t) {
    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
   }

   if (!keep) {
    synchronized (this) {
     mIdleHandlers.remove(idler);
    }
   }
  }

  // Reset the idle handler count to 0 so we do not run them again.
  pendingIdleHandlerCount = 0;

  // While calling an idle handler, a new message could have been delivered
  // so go back and look again for a pending message without waiting.
  nextPollTimeoutMillis = 0;
 }
}

void removeMessages(Handler h, int what, Object object) {
 if (h == null) {
  return;
 }

 synchronized (this) {
  Message p = mMessages;

  // Remove all messages at front.
  while (p != null && p.target == h && p.what == what
    && (object == null || p.obj == object)) {
   Message n = p.next;
   mMessages = n;
   p.recycleUnchecked();
   p = n;
  }

  // Remove all messages after front.
  while (p != null) {
   Message n = p.next;
   if (n != null) {
    if (n.target == h && n.what == what
     && (object == null || n.obj == object)) {
     Message nn = n.next;
     n.recycleUnchecked();
     p.next = nn;
     continue;
    }
   }
   p = n;
  }
 }
}

4. Message

(1)消息創(chuàng)建

Message.obtain()創(chuàng)建消息。若消息池鏈表sPool不為空,則從sPool中獲取第一個,flags標(biāo)記為UnInUse,同時從sPool中移除,sPoolSize減1;若消息池鏈表sPool為空,則new Message()

(2)消息釋放

recycle()將消息釋放,從內(nèi)部實現(xiàn)recycleUnchecked()可知,將flags標(biāo)記為InUse,其他各種狀態(tài)清零,同時將Message添加到sPool,且sPoolSize加1

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
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();
}

/**
 * Return a Message instance to the global pool.
 * <p>
 * You MUST NOT touch the Message after calling this function because it has
 * effectively been freed. It is an error to recycle a message that is currently
 * enqueued or that is in the process of being delivered to a Handler.
 * </p>
 */
public void recycle() {
 if (isInUse()) {
  if (gCheckRecycle) {
   throw new IllegalStateException("This message cannot be recycled because it "
     + "is still in use.");
  }
  return;
 }
 recycleUnchecked();
}

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
void recycleUnchecked() {
 // Mark the message as in use while it remains in the recycled object pool.
 // Clear out all other details.
 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++;
  }
 }
}

5. HandlerThread

由于Java中的Thread是沒有消息循環(huán)機(jī)制的,run()方法執(zhí)行完,線程則結(jié)束。HandlerThread通過使用Looper實現(xiàn)了消息循環(huán),只要不主動調(diào)用HandlerThread或Looper的quit()方法,循環(huán)就是一直走下去。

public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;

public HandlerThread(String name) {
 super(name);
 mPriority = Process.THREAD_PRIORITY_DEFAULT;
}

@Override
public void run() {
 mTid = Process.myTid();
 Looper.prepare();
 synchronized (this) {
  mLooper = Looper.myLooper();
  notifyAll();
 }
 Process.setThreadPriority(mPriority);
 onLooperPrepared();
 Looper.loop();
 mTid = -1;
}

public Looper getLooper() {
 if (!isAlive()) {
  return null;
 }

 // If the thread has been started, wait until the looper has been created.
 synchronized (this) {
  while (isAlive() && mLooper == null) {
   try {
    wait();
   } catch (InterruptedException e) {
   }
  }
 }
 return mLooper;
}

public boolean quit() {
 Looper looper = getLooper();
 if (looper != null) {
  looper.quit();
  return true;
 }
 return false;
}
}

總結(jié)

  • 關(guān)鍵類:HandlerThread、Handler、Looper、MessageQueue、Messaga
  • MessageQueue數(shù)據(jù)結(jié)構(gòu),鏈表。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

當(dāng)前標(biāo)題:Android消息循環(huán)機(jī)制源碼深入理解
網(wǎng)站URL:http://muchs.cn/article0/iepdio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、App開發(fā)App設(shè)計、搜索引擎優(yōu)化響應(yīng)式網(wǎng)站、微信小程序

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)