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

首頁 > 系統 > Android > 正文

Android 消息機制詳解及實例代碼

2019-12-12 03:41:37
字體:
來源:轉載
供稿:網友

Android 消息機制

1.概述

Android應用啟動時,會默認有一個主線程(UI線程),在這個線程中會關聯一個消息隊列(MessageQueue),所有的操作都會被封裝成消息隊列然后交給主線程處理。為了保證主線程不會退出,會將消息隊列的操作放在一個死循環中,程序就相當于一直執行死循環,每循環一次,從其內部的消息隊列中取出一個消息,然后回調相應的消息處理函數(handlerMessage),執行完成一個消息后則繼續循環,若消息隊列為空,線程則會阻塞等待。因此不會退出。如下圖所示:

Handler 、 Looper 、Message有啥關系?

在子線程中完成耗時操作,很多情況下需要更新UI,最常用的就是通過Handler將一個消息Post到UI線程中,然后再在Handler的handlerMessage方法中進行處理。而每個Handler都會關聯一個消息隊列(MessageQueue),Looper負責的就是創建一個MessageQueue,而每個Looper又會關聯一個線程(Looper通過ThreadLocal封裝)。默認情況下,MessageQueue只有一個,即主線程的消息隊列。

上面就是Android消息機制的基本原理,如果想了解更詳細,我們從源碼開始看。

2.源碼解讀

(1)ActivityThread主線程中啟動啟動消息循環Looper

public final class ActivityThread {  public static void main(String[] args) {    //代碼省略    //1.創建消息循環的Looper    Looper.prepareMainLooper();    ActivityThread thread = new ActivityThread();    thread.attach(false);    if (sMainThreadHandler == null) {      sMainThreadHandler = thread.getHandler();    }    AsyncTask.init();    //2.執行消息循環    Looper.loop();    throw new RuntimeException("Main thread loop unexpectedly exited");  }}

ActivityThread通過Looper.prepareMainLooper()創建主線程的消息隊列,最后執行Looper.loop()來啟動消息隊列。Handler關聯消息隊列和線程。

(2)Handler關聯消息隊列和線程

public Handler(Callback callback, boolean async) {    //代碼省略    //獲取Looper    mLooper = Looper.myLooper();    if (mLooper == null) {      throw new RuntimeException(        "Can't create handler inside thread that has not called Looper.prepare()");    }    //獲取消息隊列    mQueue = mLooper.mQueue;  }

Handler會在內部通過Looper.getLooper()方法來獲取Looper對象,并且與之關聯,并獲取消息隊列。那么Looper.getLooper()如何工作的呢?

  public static @Nullable Looper myLooper() {    return sThreadLocal.get();  }  public static @NonNull MessageQueue myQueue() {    return myLooper().mQueue;  }  public static void prepare() {    prepare(true);  }  //為當前線程設置一個Looper  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));  }  //設置UI線程的Looper  public static void prepareMainLooper() {    prepare(false);    synchronized (Looper.class) {      if (sMainLooper != null) {        throw new IllegalStateException("The main Looper has already been prepared.");      }      sMainLooper = myLooper();    }  }

在Looper類中,myLooper()方法,通過sThreadLocal.get()來獲取的,在prepareMainLooper()中調用prepare()方法,在這個方法中創建了一個Looper對象,并將對象設置了sThreadLocal()。這樣隊列就和線程關聯起來了。通過sThreadLocal.get()方法,保證不同的線程不能訪問對方的消息隊列。

為什么要更新UI的Handler必須在主線程中創建?

因為Handler要與主線程的消息隊列關聯上,這樣handlerMessage才會執行在UI線程,此時UI線程才是安全的。

(3)消息循環,消息處理

消息循環的建立就是通過Looper.loop()方法。源代碼如下:

/**   * Run the message queue in this thread. Be sure to call   * {@link #quit()} to end the loop.   */  public static void loop() {    final Looper me = myLooper();    if (me == null) {      throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");    }    //1.獲取消息隊列    final MessageQueue queue = me.mQueue;    //2.死循環,即消息循環    for (;;) {      //3.獲取消息,可能阻塞      Message msg = queue.next(); // might block      if (msg == null) {        // No message indicates that the message queue is quitting.        return;      }      //4.處理消息      msg.target.dispatchMessage(msg);      //回收消息      msg.recycleUnchecked();    }  }

從上述程序我們可以看出,loop()方法的實質上是建立一個死循環,然后通過從消息隊列中逐個取出消息,最后處理消息。對于Looper:通過Looper.prepare()來創建Looper對象(消息隊列封裝在Looper對象中),并且保存在sThreadLocal中,然后通過通過Looper.loop()進行消息循環,這兩步通常成對出現。

public final class Message implements Parcelable {  //target處理  Handler target;   //Runnable類型的callback  Runnable callback;  //下一條消息,消息隊列是鏈式存儲的  Message next;}

從源碼中可以看出,target是Handler類型。實際上就是轉了一圈,通過Handler發送消息給消息隊列,消息隊列又將消息分發給Handler處理。在Handle類中:

//消息處理函數,子類覆寫public void handleMessage(Message msg) {}private static void handleCallback(Message message) {    message.callback.run();  }//分發消息public void dispatchMessage(Message msg) {    if (msg.callback != null) {      handleCallback(msg);    } else {      if (mCallback != null) {        if (mCallback.handleMessage(msg)) {          return;        }      }      handleMessage(msg);    }  }

從上述程序可以看出,dispatchMessage只是一個分發的方法,如果Run nable類型的callback為空,則執行handleMessage來處理消息,該方法為空,我們會將更新UI的代碼寫在該函數中;如果callback不為空,則執行handleCallback來處理,該方法會調用callback的run方法。其實這是Handler分發的兩種類型,比如post(Runnable callback)則callback就不為空,當我們使用Handler來sendMessage時通常不設置callback,因此,執行handlerMessage。

 public final boolean post(Runnable r)  {    return sendMessageDelayed(getPostMessage(r), 0);  }  public String getMessageName(Message message) {    if (message.callback != null) {      return message.callback.getClass().getName();    }    return "0x" + Integer.toHexString(message.what);  }  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);  }

從上述程序可以看到,在post(Runnable r)時,會將Runnable包裝成Message對象,并且將Runnable對象設置給Message對象的callback,最后會將該對象插入消息隊列。sendMessage也是類似實現:

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

不管是post一個Runnable還是Message,都會調用sendMessageDelayed(msg, time)方法。Handler最終將消息追加到MessageQueue中,而Looper不斷地從MessageQueue中讀取消息,并且調用Handler的dispatchMessage分發消息,這樣消息就源源不斷地被產生、添加到MessageQueue、被Handler處理,Android應用就運轉起來了。

3.檢驗

new Thread(){  Handler handler = null;  public void run () {    handler = new Handler();  };}.start();

上述代碼有問題嗎?

Looper對象是ThreadLocal的,即每個線程都用自己的Looper,這個Looper可以為空。但是,當在子線程中創建Handler對象時,如果Looper為空,那么會出現異常。

public Handler(Callback callback, boolean async) {    //代碼省略    //獲取Looper    mLooper = Looper.myLooper();    if (mLooper == null) {      throw new RuntimeException(        "Can't create handler inside thread that has not called Looper.prepare()");    }    //獲取消息隊列    mQueue = mLooper.mQueue;  }

當mLooper為空時,拋出異常。這是因為Looper對象沒有創建,因此,sThreadLocal.get()會返回null。Handler的基本原理就是要與MessageQueue建立關聯,并且將消息投遞給MessageQueue,如果沒有MessageQueue,則Handler沒有存在的必要,而MessageQueue又被封住在Looper中,因此創建Handler時,Looper一定不能為空。解決辦法如下:

new Thread(){  Handler handler = null;  public void run () {    //為當前線程創建Looper,并且綁定到ThreadLocal中    Looper.prepare()    handler = new Handler();    //啟動消息循環    Looper.loop();  };}.start();

如果只創建Looper不啟動消息循環,雖然不拋出異常,但是通過handler來post或者sendMessage()也不會有效。因為雖然消息會被追加到消息隊列,但是并沒有啟動消息循環,也就不會從消息隊列中獲取消息并且執行了。

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 舞阳县| 泌阳县| 涪陵区| 武隆县| 繁昌县| 雷山县| 宣武区| 清苑县| 汉中市| 安陆市| 临汾市| 新竹市| 饶平县| 宁夏| 繁峙县| 徐闻县| 七台河市| 奉节县| 化州市| 大冶市| 桑植县| 彭州市| 黑龙江省| 泸州市| 禹州市| 全州县| 武宁县| 陕西省| 开封市| 梅河口市| 垦利县| 民和| 平湖市| 台江县| 中阳县| 青阳县| 松江区| 内丘县| 石嘴山市| 克什克腾旗| 马山县|