国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

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

2019-12-12 02:30:50
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

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

前言:

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

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

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

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

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

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

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

我們平時(shí)使用是這樣的:

 //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 {  //對(duì)于非主線程處理消息需要傳Looper,主線程有默認(rèn)的sMainLooper  public MyHandler(Looper looper) {   super(looper);  }  @Override  public void handleMessage(Message msg) {   super.handleMessage(msg);  } }

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

我們這里提到了幾個(gè)概念:

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

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

源碼解析

1. Looper

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

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

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

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

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

loop()開(kāi)始消息循環(huán)。循環(huán)過(guò)程如下:

每次從消息隊(duì)列MessageQueue中取出一個(gè)Message

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

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

循環(huán)以上步驟,若沒(méi)有消息表明消息隊(duì)列停止,退出循環(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)兩個(gè)方法。從下面源碼可以看出Runnable賦值給了Message的callback,最終也是封裝成Message對(duì)象對(duì)象。學(xué)姐個(gè)人認(rèn)為外部調(diào)用不統(tǒng)一使用Message,應(yīng)該是兼容Java的線程任務(wù),學(xué)姐認(rèn)為這種思想也可以借鑒到平常開(kāi)發(fā)過(guò)程中。發(fā)送的消息都會(huì)入隊(duì)到MessageQueue隊(duì)列中。

(2)處理消息

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

(3)移除消息

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

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)消息入隊(duì)

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

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

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

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

(2)消息輪詢

next()依次從MessageQueue中取出Message

(3)移除消息

removeMessages()可以移除消息,做的事情實(shí)際上就是將消息從鏈表移除,同時(shí)將移除的消息添加到消息池,提供循環(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中獲取第一個(gè),flags標(biāo)記為UnInUse,同時(shí)從sPool中移除,sPoolSize減1;若消息池鏈表sPool為空,則new Message()

(2)消息釋放

recycle()將消息釋放,從內(nèi)部實(shí)現(xiàn)recycleUnchecked()可知,將flags標(biāo)記為InUse,其他各種狀態(tài)清零,同時(shí)將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是沒(méi)有消息循環(huán)機(jī)制的,run()方法執(zhí)行完,線程則結(jié)束。HandlerThread通過(guò)使用Looper實(shí)現(xiàn)了消息循環(huán),只要不主動(dòng)調(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;}@Overridepublic 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),鏈表。

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

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 永定县| 泰来县| 游戏| 大足县| 玛纳斯县| 湖口县| 察雅县| 禹州市| 茌平县| 太康县| 巴青县| 绥芬河市| 尚志市| 寿光市| 崇州市| 锡林浩特市| 敦化市| 灵宝市| 云南省| 黎城县| 安国市| 红河县| 马山县| 苗栗县| 朝阳县| 兴海县| 淳化县| 年辖:市辖区| 革吉县| 清河县| 昆明市| 石河子市| 平湖市| 馆陶县| 西盟| 张北县| 永登县| 永修县| 长宁县| 青神县| 定西市|