解析非同步訊息處理機制

來源:互聯網
上載者:User

標籤:

一、概述
      Android非同步訊息處理機制主要由四個部分組成,Message、Handle、MessageQueue和Looper。下面我就對這四個部分進行一下簡要的介紹。
1.Message
     Message是線上程之間傳遞的訊息,它可以在內部攜帶少量的資訊,用於在不同線程之間交換資料。
2.MessageQueue
     MessageQueue 是訊息佇列,它主要用於存放所有由 Handler 發送過來的訊息,這部分訊息會一直在訊息佇列中,等待被處理。每個線程中只會有一個 MessageQueue 對象。
3.Handle
     Handler 顧名思義也就是處理者的意思,它主要用於發送和處理訊息。 發送訊息一般使用 handler 的 sendMessage()方法,處理訊息會調用 handleMessage() 方法。
4.Looper
     Looper 是每個線程中 MessageQueue 的管家, 調用 loop() 方法後,就會進入到一個無限迴圈當中,然後每當發現 MessageQueue 中存在一條訊息,就會將其取出,並傳遞到 handleMessage()方法當中。每個線程中也只會有一個Looper對象。
     瞭解了Message、Handle、MessageQueue以及Looper的基本概念之後,我們再來對非同步訊息處理的整個過程梳理一遍。首先需要在主線程當中建立一個Handle對象,並重寫handleMessage()方法。然後當子線程中需要進行UI操作時,就建立一個Message對象,並通過Handle將這條訊息發送出去。之後這條資訊會被添加到MessageQueue的隊列中等待被處理,而Looper則會一直嘗試從MessageQueue中取出待處理訊息,最後分發回Handle的handleMessage()方法中。由於Handle是在主線程中建立的,所以此時handleMessage()方法中的代碼也會在主線程中運行,於是我們在這裡就可以安心地進行UI操作了。整個非同步訊息處理機制的流程。

       一條Message經過這樣一個流程的輾轉調用後,也就是從子線程進入到主線程,從不能更新UI變成了可以更新UI,整個非同步訊息處理機制的核心思想也就是如此了。

二、詳細介紹
1、Looper
    對於Looper主要是prepare()和loop()兩個方法。

public static final void prepare() {          if (sThreadLocal.get() != null) {              throw new RuntimeException("Only one Looper may be created per thread");          }          sThreadLocal.set(new Looper(true));  }

     sThreadLocal是一個ThreadLocal對象,可以在一個線程中儲存變數。Looper 就是儲存在sThreadLocal裡面。這個方法被調用後,首先會判斷當前線程裡面有沒有 Looper對象,如果沒有就會建立一個 Looper 對象,如果存在則會拋出異常。可見,prepare()方法,不能被調用兩次。這就保證了一個線程只有一個Looper對象。
接下來我們看一下Looper的建構函式:

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

在 Looper 的建構函式中,建立了 MessageQueue 對象,這也保證了一個線程只有一個 MessageQueue 對象。
然後我們看看 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.");        }        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.recycle();        }}

這個方法先調用 myLooper() 方法,得到 sThreadLocal 中儲存的 Looper 對象,並得到 looper 對象對應的 MessageQueue 對象,然後就進入無限迴圈。
該迴圈主要包括:取出一條訊息,如果沒有訊息則阻塞; 調用 msg.target.dispatchMessage(msg);把訊息交給msg的target的dispatchMessage方法去處理。
Looper主要作用:
1、 與當前線程綁定,保證一個線程只會有一個Looper執行個體,同時一個Looper執行個體也只有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取訊息,交給訊息的target屬性的dispatchMessage去處理。
2、Handler
在使用Handler之前,我們都是初始化一個執行個體,比如用於更新UI線程,我們會在聲明的時候直接初始化,或者在onCreate中初始化Handler執行個體

private Handler mHandler = new Handler()    {        public void handleMessage(android.os.Message msg)        {            switch (msg.what)            {            case value:                break;            default:                break;            }        };    };

三、小結
1、首先Looper.prepare()在本線程中儲存一個Looper執行個體,然後該執行個體中儲存一個MessageQueue對象;因為Looper.prepare()在一個線程中只能調用一次,所以MessageQueue在一個線程中只會存在一個。大家可能還會問,那麼在Activity中,我們並沒有顯示的調用Looper.prepare()和Looper.loop()方法,為啥Handler可以成功建立呢,這是因為在Activity的啟動代碼中,已經在當前UI線程調用了Looper.prepare()和Looper.loop()方法
2、Looper.loop()會讓當前線程進入一個無限迴圈,不端從MessageQueue的執行個體中讀取訊息,然後回調msg.target.dispatchMessage(msg)方法。
3、Handler的構造方法,會首先得到當前線程中儲存的Looper執行個體,並與Looper執行個體中的MessageQueue相關聯。
4、Handler的sendMessage方法,會給msg的target賦值為handler自身,然後加入MessageQueue中。
5、在構造Handler執行個體時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法

 

查看原文http://blog.csdn.net/applydev

解析非同步訊息處理機制

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.