欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

代碼分析Android消息機(jī)制

 更新時(shí)間:2018年03月18日 16:14:01   作者:mmmmar  
本文通過(guò)代碼實(shí)例詳細(xì)分析了Android消息機(jī)制的相關(guān)知識(shí)點(diǎn),對(duì)此有需要的朋友可以參考學(xué)習(xí)下。

我們知道在編程時(shí)許多操作(如更新UI)需要在主線程中完成,而且,耗時(shí)操作(如網(wǎng)絡(luò)連接)需要放在子線程中,否則會(huì)引起ANR。所以我們常使用Handler來(lái)實(shí)現(xiàn)線程間的消息傳遞,這里討論的也就是Handler的運(yùn)行機(jī)制。

Handler的運(yùn)行主要由兩個(gè)類(lèi)來(lái)支撐:Looper與MessageQueue。熟悉開(kāi)發(fā)的朋友都知道在子線程中默認(rèn)是無(wú)法創(chuàng)建Handler的,這是因?yàn)樽泳€程中不存在消息隊(duì)列。當(dāng)需要?jiǎng)?chuàng)建一個(gè)與子線程綁定的Handler時(shí),標(biāo)準(zhǔn)代碼如下:

class LooperThread extends Thread {
  public Handler mHandler;
  public void run() {
    Looper.prepare();
    mHandler = new Handler() {
      public void handleMessage(Message msg) {
        // process incoming messages here
      }
    };
    Looper.loop();
   }
}

在創(chuàng)建Handler前,需要先調(diào)用Looper.prepare()方法,之后再調(diào)用Looper.loop()方法。也就是說(shuō)Handler的功能實(shí)現(xiàn)建立在Looper之上。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;

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

private Looper(boolean quitAllowed) {
  mQueue = new MessageQueue(quitAllowed);
  mThread = Thread.currentThread();
}

由于Looper的消息循環(huán)是一個(gè)死循環(huán),一個(gè)線程最多只能有一個(gè)Looper,所以Looper.prepare()函數(shù)首先檢查該線程是否已經(jīng)擁有一個(gè)Looper,如果有則拋出異常。Looper通過(guò)ThreadLocal類(lèi)為每個(gè)線程儲(chǔ)存獨(dú)立的Looper實(shí)例,簡(jiǎn)單說(shuō)一下ThreadLocal的實(shí)現(xiàn)原理:

Java并發(fā)編程:深入剖析ThreadLocal

首先,在每個(gè)線程Thread內(nèi)部有一個(gè)ThreadLocal.ThreadLocalMap類(lèi)型的成員變量threadLocals,這個(gè)threadLocals就是用來(lái)存儲(chǔ)實(shí)際的變量副本的,鍵值為當(dāng)前ThreadLocal變量,value為變量副本。

初始時(shí),在Thread里面,threadLocals為空,當(dāng)通過(guò)ThreadLocal變量調(diào)用get()方法或者set()方法,就會(huì)對(duì)Thread類(lèi)中的threadLocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。

然后在當(dāng)前線程里面,如果要使用副本變量,就可以通過(guò)get方法在threadLocals里面查找。

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;
  for (;;) {
    Message msg = queue.next(); // might block
    if (msg == null) {
      // No message indicates that the message queue is quitting.
      return;
    }
    msg.target.dispatchMessage(msg);
    msg.recycleUnchecked();
  }
}

在4行可以看到一個(gè)我們熟悉的異常信息,說(shuō)明并沒(méi)有Looper與當(dāng)前線程相關(guān)聯(lián),也就無(wú)法進(jìn)行消息傳遞。Looper.loop()方法本身是一個(gè)死循環(huán),不斷在MessageQueue中取出Message對(duì)象進(jìn)行處理,然后調(diào)用Message.recycleUnchecked()方法對(duì)其回收,這也是為什么官方推薦使用Message.obtain()方法來(lái)獲取Message實(shí)例,而不是直接新建對(duì)象。當(dāng)沒(méi)有消息可處理時(shí),MessageQueue.next()方法將阻塞,直到新的消息到來(lái)。

對(duì)于MessageQueue,我們只需要關(guān)注兩個(gè)函數(shù)即可,一個(gè)是MessageQueue.enqueueMessage()另一個(gè)是MessageQueue.next(),它們分別對(duì)應(yīng)著隊(duì)列的插入與取出操作。MessageQueue中隊(duì)列是使用單鏈表實(shí)現(xiàn)的,由Message.next屬性指向其下一個(gè)元素。

boolean enqueueMessage(Message msg, long when) {
  synchronized (this) {
    msg.markInUse();
    msg.when = when;
    Message p = mMessages;
    boolean needWake;
    if (p == null || when == 0 || when < p.when) {
      msg.next = p;
      mMessages = msg;
    } else {
      Message prev;
      for (;;) {
        prev = p;
        p = p.next;
        if (p == null || when < p.when) {
          break;
        }
      }
      msg.next = p; // invariant: p == prev.next
      prev.next = msg;
    }
  }
  return true;
}

向MessageQueue中插入元素時(shí),需要根據(jù)Message.when屬性的大小決定插入的位置,它代表了Meesage需要被處理的時(shí)間,拿Handler.sendMessage()函數(shù)為例。

public final boolean sendMessage(Message msg) {
  return sendMessageDelayed(msg, 0);
}
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;
  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);
}

從調(diào)用流程來(lái)看,Handler.sendMessage()函數(shù)其實(shí)就是向MessageQueue的消息隊(duì)列中插入了一個(gè)Message.when屬性為當(dāng)前時(shí)間的元素。

對(duì)于MessageQueue.next()函數(shù),簡(jiǎn)單來(lái)說(shuō)它的作用就是在MessageQueue的頭部取出元素,然后執(zhí)行Handler.dispatchMessage()函數(shù)。

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

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

如果我們使用Handler.post()函數(shù)發(fā)送一個(gè)Runnable對(duì)象,那么最終Runnable對(duì)象會(huì)在Handler.handleCallback()函數(shù)中執(zhí)行。如果是一個(gè)普通Message,那么它會(huì)被分發(fā)到一個(gè)我們熟悉的函數(shù)中,Handler.handleMessage(),這就是為什么一般我們都需要重寫(xiě)這個(gè)函數(shù)對(duì)消息進(jìn)行處理。

相關(guān)文章

  • Android 10 啟動(dòng)之servicemanager源碼解析

    Android 10 啟動(dòng)之servicemanager源碼解析

    這篇文章主要為大家介紹了Android 10 啟動(dòng)之servicemanager源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Kotlin協(xié)程的啟動(dòng)方式介紹

    Kotlin協(xié)程的啟動(dòng)方式介紹

    這篇文章我們來(lái)講協(xié)程的啟動(dòng),畢竟協(xié)程是一個(gè)很強(qiáng)大的設(shè)計(jì)模式,深入了解需要花很多的時(shí)間,我們先從簡(jiǎn)單開(kāi)始,其實(shí)學(xué)會(huì)了簡(jiǎn)單的使用,基本已經(jīng)可以滿(mǎn)足我們平時(shí)的開(kāi)發(fā)需要了,話(huà)不多說(shuō),開(kāi)始
    2022-09-09
  • Android使用手勢(shì)監(jiān)聽(tīng)器GestureDetector遇到的不響應(yīng)問(wèn)題

    Android使用手勢(shì)監(jiān)聽(tīng)器GestureDetector遇到的不響應(yīng)問(wèn)題

    這篇文章主要介紹了Android使用手勢(shì)監(jiān)聽(tīng)器GestureDetector遇到的不響應(yīng)問(wèn)題,具有很好的參考價(jià)值,對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • Android控件之RatingBar自定義星級(jí)評(píng)分樣式

    Android控件之RatingBar自定義星級(jí)評(píng)分樣式

    RatingBar為評(píng)分條控件,默認(rèn)效果為若干個(gè)綠色的星星,如果想將其換成其他自定義圖片就要自定義它的style。接下來(lái)通過(guò)本文給大家介紹Android控件之RatingBar自定義星級(jí)評(píng)分樣式,感興趣的朋友一起學(xué)習(xí)吧
    2016-02-02
  • Android應(yīng)用自動(dòng)跳轉(zhuǎn)到應(yīng)用市場(chǎng)詳情頁(yè)面的方法

    Android應(yīng)用自動(dòng)跳轉(zhuǎn)到應(yīng)用市場(chǎng)詳情頁(yè)面的方法

    最近在工作中遇到一個(gè)需求,推廣部門(mén)要求實(shí)現(xiàn)應(yīng)用自動(dòng)跳轉(zhuǎn)到應(yīng)用市場(chǎng)詳情頁(yè)面,通過(guò)查找一些資料,實(shí)現(xiàn)出來(lái)了,覺(jué)得有必要整理下方便以后或者有需要的朋友們參考借鑒,下面來(lái)一起詳細(xì)看看Android應(yīng)用自動(dòng)跳轉(zhuǎn)到應(yīng)用市場(chǎng)詳情頁(yè)面的方法吧。
    2016-12-12
  • Android:“萬(wàn)能”Activity重構(gòu)篇

    Android:“萬(wàn)能”Activity重構(gòu)篇

    本文主要介紹了mvp以及每一層,以及使用mvp來(lái)重構(gòu)“萬(wàn)能”Activity,其實(shí)每一層需要注意的東西還有很多,比如model層是最難寫(xiě)的一層。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • Android應(yīng)用禁止屏幕休眠的3種方法

    Android應(yīng)用禁止屏幕休眠的3種方法

    這篇文章主要為大家詳細(xì)介紹了Android應(yīng)用禁止屏幕休眠的3種方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 怎么發(fā)布打包并發(fā)布自己的Android應(yīng)用(APP)

    怎么發(fā)布打包并發(fā)布自己的Android應(yīng)用(APP)

    前面我為大家講的都是關(guān)于Android開(kāi)發(fā)方面的知識(shí)點(diǎn)和技術(shù),不少朋友可能會(huì)感到疑惑--究竟我該怎么打包、發(fā)布自己開(kāi)發(fā)的APP,怎樣將我的APP放到網(wǎng)上工別人下載,怎樣保證我的APP安全及版權(quán)問(wèn)題呢
    2013-11-11
  • Android Studio手動(dòng)打包的教程圖解

    Android Studio手動(dòng)打包的教程圖解

    項(xiàng)目寫(xiě)完了,現(xiàn)在需要把應(yīng)用上傳到市場(chǎng)上面,那么怎么把項(xiàng)目打包成apk呢?下面腳本之家小編給大家?guī)?lái)了Android Studio手動(dòng)打包的方法,一起看看吧
    2018-07-07
  • Android實(shí)現(xiàn)房貸計(jì)算器功能

    Android實(shí)現(xiàn)房貸計(jì)算器功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)房貸計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評(píng)論