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

Android 消息機制

創(chuàng)新互聯(lián)公司專注于回民企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站開發(fā)?;孛窬W(wǎng)站建設(shè)公司,為回民等地區(qū)提供建站服務(wù)。全流程定制制作,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)

1.概述

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

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

Handler 、 Looper 、Message有啥關(guān)系?

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

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

2.源碼解讀

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

public final class ActivityThread {
  public static void main(String[] args) {
    //代碼省略
    //1.創(chuàng)建消息循環(huán)的Looper
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
      sMainThreadHandler = thread.getHandler();
    }
    AsyncTask.init();

    //2.執(zhí)行消息循環(huán)
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
  }
}

ActivityThread通過Looper.prepareMainLooper()創(chuàng)建主線程的消息隊列,最后執(zhí)行Looper.loop()來啟動消息隊列。Handler關(guān)聯(lián)消息隊列和線程。

(2)Handler關(guān)聯(lián)消息隊列和線程

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會在內(nèi)部通過Looper.getLooper()方法來獲取Looper對象,并且與之關(guān)聯(lián),并獲取消息隊列。那么Looper.getLooper()如何工作的呢?

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

  public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
  }
  public static void prepare() {
    prepare(true);
  }
  //為當前線程設(shè)置一個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));
  }
  //設(shè)置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()中調(diào)用prepare()方法,在這個方法中創(chuàng)建了一個Looper對象,并將對象設(shè)置了sThreadLocal()。這樣隊列就和線程關(guān)聯(lián)起來了。通過sThreadLocal.get()方法,保證不同的線程不能訪問對方的消息隊列。

為什么要更新UI的Handler必須在主線程中創(chuàng)建?

因為Handler要與主線程的消息隊列關(guān)聯(lián)上,這樣handlerMessage才會執(zhí)行在UI線程,此時UI線程才是安全的。

(3)消息循環(huán),消息處理

消息循環(huán)的建立就是通過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.死循環(huán),即消息循環(huán)
    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()方法的實質(zhì)上是建立一個死循環(huán),然后通過從消息隊列中逐個取出消息,最后處理消息。對于Looper:通過Looper.prepare()來創(chuàng)建Looper對象(消息隊列封裝在Looper對象中),并且保存在sThreadLocal中,然后通過通過Looper.loop()進行消息循環(huán),這兩步通常成對出現(xiàn)。

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

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

//消息處理函數(shù),子類覆寫
public void handleMessage(Message msg) {
}

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

//分發(fā)消息
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
      handleCallback(msg);
    } else {
      if (mCallback != null) {
        if (mCallback.handleMessage(msg)) {
          return;
        }
      }
      handleMessage(msg);
    }
  }

從上述程序可以看出,dispatchMessage只是一個分發(fā)的方法,如果Run nable類型的callback為空,則執(zhí)行handleMessage來處理消息,該方法為空,我們會將更新UI的代碼寫在該函數(shù)中;如果callback不為空,則執(zhí)行handleCallback來處理,該方法會調(diào)用callback的run方法。其實這是Handler分發(fā)的兩種類型,比如post(Runnable callback)則callback就不為空,當我們使用Handler來sendMessage時通常不設(shè)置callback,因此,執(zhí)行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對象設(shè)置給Message對象的callback,最后會將該對象插入消息隊列。sendMessage也是類似實現(xiàn):

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

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

3.檢驗

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

上述代碼有問題嗎?

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

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對象沒有創(chuàng)建,因此,sThreadLocal.get()會返回null。Handler的基本原理就是要與MessageQueue建立關(guān)聯(lián),并且將消息投遞給MessageQueue,如果沒有MessageQueue,則Handler沒有存在的必要,而MessageQueue又被封住在Looper中,因此創(chuàng)建Handler時,Looper一定不能為空。解決辦法如下:

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

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

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

標題名稱:Android消息機制詳解及實例代碼
URL網(wǎng)址:http://bm7419.com/article28/ipopjp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、動態(tài)網(wǎng)站、企業(yè)網(wǎng)站制作、電子商務(wù)、小程序開發(fā)、服務(wù)器托管

廣告

聲明:本網(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è)網(wǎng)站維護公司