Android的HandlerLooperMessage機(jī)制應(yīng)用實(shí)例與詳解(二)

    上一篇博文給出了Android中基于Handler Looper機(jī)制實(shí)現(xiàn)線(xiàn)程間通信的兩個(gè)典型實(shí)例。本文將對(duì)該機(jī)制的基本原理進(jìn)行較深入的研究。個(gè)人認(rèn)為,學(xué)好Android編程最好的老師就是Android的源代碼,下面將基于A(yíng)ndroid-19的源碼進(jìn)行分析,重點(diǎn)闡述分析思路。

成都創(chuàng)新互聯(lián)公司主要從事成都做網(wǎng)站、網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)威寧,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專(zhuān)業(yè),歡迎來(lái)電咨詢(xún)建站服務(wù):13518219792

    要分析Handler Looper機(jī)制,自然想到去看Handler類(lèi)和Looper類(lèi)的源碼(分別位于Handler.java和Looper.java兩個(gè)文件中)。簡(jiǎn)單閱讀兩個(gè)類(lèi)的描述后,在Looper類(lèi)的描述中能找到以下一段示例代碼。

* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
*  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();
*      }
*  }</pre>
*/

    這段代碼給出了Handler Looper機(jī)制實(shí)現(xiàn)進(jìn)程間通信的三大基本步驟,包括Looper的兩個(gè)函數(shù)prepare()和loop(),以及Handler的handleMessage函數(shù)。上一篇博文中實(shí)例二模擬子線(xiàn)程向UI主線(xiàn)程傳遞信息的程序就基本上是直接copy這段示例代碼實(shí)現(xiàn)的。

    先看第一個(gè)步驟:調(diào)用Looper.prepare()函數(shù),猜測(cè)應(yīng)該是創(chuàng)建Looper對(duì)象,做些初始化工作。代碼如下:

/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
    prepare(true);
}

    直接調(diào)用了重載函數(shù)prepare(true);

//ThreadLocal實(shí)例為多個(gè)線(xiàn)程共享,但保證每個(gè)線(xiàn)程的存儲(chǔ)空間相互獨(dú)立
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 
//消息隊(duì)列
final MessageQueue mQueue;
//當(dāng)前線(xiàn)程引用
final Thread mThread;
private static void prepare(boolean quitAllowed) {
    //保證一個(gè)線(xiàn)程最多只能創(chuàng)建一個(gè)Looper對(duì)象。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //創(chuàng)建Looper對(duì)象并存儲(chǔ)到當(dāng)前線(xiàn)程獨(dú)立的存儲(chǔ)空間中
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
        //創(chuàng)建消息隊(duì)列(線(xiàn)程間即通過(guò)該消息隊(duì)列實(shí)現(xiàn)通信)
        mQueue = new MessageQueue(quitAllowed);
        //獲得當(dāng)前線(xiàn)程的引用
        mThread = Thread.currentThread();
    }

    看到這里明白了,Looper的prepare函數(shù)實(shí)際上創(chuàng)建了Looper對(duì)象并把對(duì)象保存到當(dāng)前線(xiàn)程獨(dú)立的存儲(chǔ)空間中。這里,Looper的構(gòu)造函數(shù)是私有的,所以外部無(wú)法直接通過(guò)new Looper()隨意創(chuàng)建Looper對(duì)象。而只能通過(guò)Looper的prepare()函數(shù)創(chuàng)建。這樣做能夠保證對(duì)于某一個(gè)線(xiàn)程,最多只會(huì)創(chuàng)建一個(gè)Looper對(duì)象的實(shí)例,這實(shí)際上就是設(shè)計(jì)模擬中的單體模式。此外,在Looper的私有構(gòu)造函數(shù)中還創(chuàng)建了消息隊(duì)列并獲得當(dāng)前線(xiàn)程(即創(chuàng)建Looper的線(xiàn)程)的引用。

    先跳過(guò)Handler.handleMessage(Message msg),直接看Looper.loop()的實(shí)現(xiàn)。

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {ui
    //myLooper()函數(shù)通過(guò)sThreadLocal.get()判斷當(dāng)前線(xiàn)程是否已經(jīng)創(chuàng)建了Looper的實(shí)例
    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();

    //這里開(kāi)始無(wú)限循環(huán)
    for (;;) {
        //從消息隊(duì)列中取出一條消息
        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);
        }
        //這里是關(guān)鍵,調(diào)用了dispatchMessage函數(shù)對(duì)從消息隊(duì)列取出的msg進(jìn)行分派
        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();
    }
}

    到這里,Looper扮演的角色已經(jīng)明朗了,主要就是通過(guò)loop()函數(shù)中的那個(gè)無(wú)限循環(huán)不斷從消息隊(duì)列中取出消息,并通過(guò)dispatchMessage()方法將消息派送出去。那么消息隊(duì)列中的消息從哪里來(lái),又會(huì)被派送到哪里呢?

    先來(lái)分析第一個(gè)問(wèn)題,消息從哪里來(lái)。上一篇博文的實(shí)例中,消息源線(xiàn)程均通過(guò)調(diào)用Handler的sendMessage()函數(shù)來(lái)發(fā)送消息。進(jìn)入Handler.java文件看其中的sendMessage()函數(shù)。  

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

    這里調(diào)用了sendMessageDelayed,延時(shí)0毫秒。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
   if (delayMillis < 0) {
       delayMillis = 0;
   }
   return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

    進(jìn)一步調(diào)用了sendMessageAtTime,在當(dāng)前時(shí)刻發(fā)出。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
   //獲得消息隊(duì)列的引用
   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);
 }

    再看enqueueMessage函數(shù)。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
   //設(shè)置target handler
   msg.target = this;
   if (mAsynchronous) {
        msg.setAsynchronous(true);
   }
   //將消息插入到消息隊(duì)列中
   return queue.enqueueMessage(msg, uptimeMillis);
}

    看到這里已經(jīng)明朗了,消息源線(xiàn)程通過(guò)Handler.sendMessage發(fā)送消息,實(shí)際上就是把消息插入了與之關(guān)聯(lián)的消息隊(duì)列中。在enqueueMessage函數(shù)中有一條關(guān)鍵語(yǔ)句msg.target = this,通過(guò)這條語(yǔ)句就把Handler和Looper關(guān)聯(lián)起來(lái)了(在Looper.loop()的循環(huán)中就是通過(guò)msg.target屬性找到發(fā)送消息的Handler并調(diào)用其dispatchMessage()函數(shù)派發(fā)消息的).

    搞清楚了消息從哪里來(lái),接下來(lái)分析消息被派發(fā)到哪里,接著看dispatchMessage()函數(shù)的實(shí)現(xiàn)。

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
    //若消息對(duì)象實(shí)現(xiàn)了其中的Runnable接口,調(diào)用對(duì)應(yīng)的回調(diào)函數(shù),即為message.callback.run())
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //若實(shí)現(xiàn)了Handler類(lèi)的Callback接口,調(diào)用接口的回調(diào)函數(shù)
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //將消息返回Handler的消息處理回調(diào)函數(shù)(是Handler的成員函數(shù),示例代碼中回調(diào)的就是該函數(shù))
        handleMessage(msg);
    }
}

    可見(jiàn),dispatchMessage函數(shù)中根據(jù)msg,handler對(duì)象的設(shè)置情況調(diào)用相應(yīng)的消息處理回調(diào)函數(shù),我們只需要在這個(gè)回調(diào)函數(shù)中添加代碼,就可以進(jìn)行消息處理。示例代碼的第二個(gè)步驟中的handleMessage函數(shù)就是在這里被回調(diào)的。

    下面回到示例代碼中的第二個(gè)步驟:

*    mHandler = new Handler() {
*         public void handleMessage(Message msg) {
*         // process incoming messages here
*         }
*    };

   這段代碼創(chuàng)建了Handler的對(duì)象,并覆蓋了其中的HandleMessage方法,用戶(hù)可以添加自己的消息處理函數(shù)。 handleMessage回調(diào)函數(shù)上面已經(jīng)分析過(guò)了,下面主要看看創(chuàng)建handler對(duì)象時(shí)都做了哪些事情。轉(zhuǎn)入Handler.java文件,看Handler的構(gòu)造函數(shù)(找不帶參數(shù)那個(gè))。

/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
    this(null, false);
}

    調(diào)用了下面的雙參數(shù)的構(gòu)造函數(shù)。

final Looper mLooper;
final MessageQueue mQueue;
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {
           Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());
        }
    }
    //獲得當(dāng)前線(xiàn)程Looper對(duì)象的引用
    mLooper = Looper.myLooper();
    if (mLooper == null) {
         throw new RuntimeException(
             "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //獲得Looper關(guān)聯(lián)的消息隊(duì)列    
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

    到這里發(fā)現(xiàn)Handler的構(gòu)造函數(shù)主要做了兩件事:1)獲得當(dāng)前線(xiàn)程Looper對(duì)象的應(yīng)用.2)獲得與Looper關(guān)聯(lián)的消息隊(duì)列的引用。到這里,Handler、Looper、消息隊(duì)列三者就已經(jīng)關(guān)聯(lián)起來(lái)了。

    下面通過(guò)一個(gè)示意圖對(duì)上面的分析進(jìn)行總結(jié)。

Android的Handler Looper Message機(jī)制應(yīng)用實(shí)例與詳解(二)

    由圖可見(jiàn),基于Handler Looper機(jī)制傳遞消息主要包括以下幾個(gè)步驟。

    (1)目標(biāo)線(xiàn)程調(diào)用Looper.prepare()創(chuàng)建Looper對(duì)象和消息隊(duì)列。

    (2)目標(biāo)線(xiàn)程通過(guò)new Handler()創(chuàng)建handler對(duì)象,將Handler,Looper,消息隊(duì)列三者關(guān)聯(lián)起來(lái)。并覆蓋其handleMessage函數(shù)。

    (3)目標(biāo)線(xiàn)程調(diào)用Looper.loop()監(jiān)聽(tīng)消息隊(duì)列。

    (4)消息源線(xiàn)程調(diào)用Handler.sendMessage發(fā)送消息。

    (5)消息源線(xiàn)程調(diào)用MessageQueue.enqueueMessage將待發(fā)消息插入消息隊(duì)列。

    (6)目標(biāo)線(xiàn)程的loop()檢測(cè)到消息隊(duì)列有消息插入,將其取出。

    (7)目標(biāo)線(xiàn)程將取出的消息通過(guò)Handler.dispatchMessage派發(fā)給Handler.handleMessage進(jìn)行消息處理。

    到這里整個(gè)Android的Handler Looper機(jī)制傳遞消息原理就分析完畢了。還有一個(gè)問(wèn)題值得一提,回顧一下上一篇博文的示例1模擬從網(wǎng)絡(luò)上下載數(shù)據(jù)的程序,UI主線(xiàn)程只通過(guò)new Handler()創(chuàng)建了Handler對(duì)象的實(shí)例并覆蓋了其handleMessage函數(shù)。在代碼中并沒(méi)有看到調(diào)用Looper.prepare和Looper.loop(),那么UI主線(xiàn)程中沒(méi)有創(chuàng)建Looper對(duì)象嗎?下面就來(lái)分析這個(gè)問(wèn)題,既然是UI主線(xiàn)程,那么自然是在啟動(dòng)應(yīng)用時(shí)候由系統(tǒng)自動(dòng)創(chuàng)建的,創(chuàng)建過(guò)程中是否已經(jīng)創(chuàng)建了Looper對(duì)象并調(diào)用loop()進(jìn)行監(jiān)聽(tīng)了呢?轉(zhuǎn)到ActivityThread.java,找到其中的main函數(shù),這里即為Android程序的入口。在其中能看到以下兩行代碼。

Looper.prepareMainLooper();
    ...... ...... 
Looper.loop();

    再看prepareMainLooper函數(shù)的實(shí)現(xiàn):

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
       if (sMainLooper != null) {
           throw new IllegalStateException("The main Looper has already been prepared.");
       }
       sMainLooper = myLooper();
    }
}

    在這里調(diào)用了prepare創(chuàng)建Looper對(duì)象。所以說(shuō),對(duì)于UI主線(xiàn)程而言,其Looper對(duì)象是由系統(tǒng)創(chuàng)建好的,用戶(hù)就無(wú)需自行創(chuàng)建了。  

    

新聞名稱(chēng):Android的HandlerLooperMessage機(jī)制應(yīng)用實(shí)例與詳解(二)
分享網(wǎng)址:http://www.muchs.cn/article32/gcegpc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)頁(yè)設(shè)計(jì)公司、做網(wǎng)站微信小程序、企業(yè)網(wǎng)站制作、自適應(yīng)網(wǎng)站、手機(jī)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都app開(kāi)發(fā)公司