91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android的Handler Looper Message機制應用實例與詳解(二)

發布時間:2020-06-21 12:43:26 來源:網絡 閱讀:1759 作者:sprocessor 欄目:移動開發

    上一篇博文給出了Android中基于Handler Looper機制實現線程間通信的兩個典型實例。本文將對該機制的基本原理進行較深入的研究。個人認為,學好Android編程最好的老師就是Android的源代碼,下面將基于Android-19的源碼進行分析,重點闡述分析思路。

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

* <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機制實現進程間通信的三大基本步驟,包括Looper的兩個函數prepare()和loop(),以及Handler的handleMessage函數。上一篇博文中實例二模擬子線程向UI主線程傳遞信息的程序就基本上是直接copy這段示例代碼實現的。

    先看第一個步驟:調用Looper.prepare()函數,猜測應該是創建Looper對象,做些初始化工作。代碼如下:

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

    直接調用了重載函數prepare(true);

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

private Looper(boolean quitAllowed) {
        //創建消息隊列(線程間即通過該消息隊列實現通信)
        mQueue = new MessageQueue(quitAllowed);
        //獲得當前線程的引用
        mThread = Thread.currentThread();
    }

    看到這里明白了,Looper的prepare函數實際上創建了Looper對象并把對象保存到當前線程獨立的存儲空間中。這里,Looper的構造函數是私有的,所以外部無法直接通過new Looper()隨意創建Looper對象。而只能通過Looper的prepare()函數創建。這樣做能夠保證對于某一個線程,最多只會創建一個Looper對象的實例,這實際上就是設計模擬中的單體模式。此外,在Looper的私有構造函數中還創建了消息隊列并獲得當前線程(即創建Looper的線程)的引用。

    先跳過Handler.handleMessage(Message msg),直接看Looper.loop()的實現。

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {ui
    //myLooper()函數通過sThreadLocal.get()判斷當前線程是否已經創建了Looper的實例
    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);
        }
        //這里是關鍵,調用了dispatchMessage函數對從消息隊列取出的msg進行分派
        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扮演的角色已經明朗了,主要就是通過loop()函數中的那個無限循環不斷從消息隊列中取出消息,并通過dispatchMessage()方法將消息派送出去。那么消息隊列中的消息從哪里來,又會被派送到哪里呢?

    先來分析第一個問題,消息從哪里來。上一篇博文的實例中,消息源線程均通過調用Handler的sendMessage()函數來發送消息。進入Handler.java文件看其中的sendMessage()函數。  

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

    這里調用了sendMessageDelayed,延時0毫秒。

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

    進一步調用了sendMessageAtTime,在當前時刻發出。

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

    再看enqueueMessage函數。

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

    看到這里已經明朗了,消息源線程通過Handler.sendMessage發送消息,實際上就是把消息插入了與之關聯的消息隊列中。在enqueueMessage函數中有一條關鍵語句msg.target = this,通過這條語句就把Handler和Looper關聯起來了(在Looper.loop()的循環中就是通過msg.target屬性找到發送消息的Handler并調用其dispatchMessage()函數派發消息的).

    搞清楚了消息從哪里來,接下來分析消息被派發到哪里,接著看dispatchMessage()函數的實現。

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

    可見,dispatchMessage函數中根據msg,handler對象的設置情況調用相應的消息處理回調函數,我們只需要在這個回調函數中添加代碼,就可以進行消息處理。示例代碼的第二個步驟中的handleMessage函數就是在這里被回調的。

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

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

   這段代碼創建了Handler的對象,并覆蓋了其中的HandleMessage方法,用戶可以添加自己的消息處理函數。 handleMessage回調函數上面已經分析過了,下面主要看看創建handler對象時都做了哪些事情。轉入Handler.java文件,看Handler的構造函數(找不帶參數那個)。

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

    調用了下面的雙參數的構造函數。

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());
        }
    }
    //獲得當前線程Looper對象的引用
    mLooper = Looper.myLooper();
    if (mLooper == null) {
         throw new RuntimeException(
             "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //獲得Looper關聯的消息隊列    
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

    到這里發現Handler的構造函數主要做了兩件事:1)獲得當前線程Looper對象的應用.2)獲得與Looper關聯的消息隊列的引用。到這里,Handler、Looper、消息隊列三者就已經關聯起來了。

    下面通過一個示意圖對上面的分析進行總結。

Android的Handler Looper Message機制應用實例與詳解(二)

    由圖可見,基于Handler Looper機制傳遞消息主要包括以下幾個步驟。

    (1)目標線程調用Looper.prepare()創建Looper對象和消息隊列。

    (2)目標線程通過new Handler()創建handler對象,將Handler,Looper,消息隊列三者關聯起來。并覆蓋其handleMessage函數。

    (3)目標線程調用Looper.loop()監聽消息隊列。

    (4)消息源線程調用Handler.sendMessage發送消息。

    (5)消息源線程調用MessageQueue.enqueueMessage將待發消息插入消息隊列。

    (6)目標線程的loop()檢測到消息隊列有消息插入,將其取出。

    (7)目標線程將取出的消息通過Handler.dispatchMessage派發給Handler.handleMessage進行消息處理。

    到這里整個Android的Handler Looper機制傳遞消息原理就分析完畢了。還有一個問題值得一提,回顧一下上一篇博文的示例1模擬從網絡上下載數據的程序,UI主線程只通過new Handler()創建了Handler對象的實例并覆蓋了其handleMessage函數。在代碼中并沒有看到調用Looper.prepare和Looper.loop(),那么UI主線程中沒有創建Looper對象嗎?下面就來分析這個問題,既然是UI主線程,那么自然是在啟動應用時候由系統自動創建的,創建過程中是否已經創建了Looper對象并調用loop()進行監聽了呢?轉到ActivityThread.java,找到其中的main函數,這里即為Android程序的入口。在其中能看到以下兩行代碼。

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

    再看prepareMainLooper函數的實現:

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

    在這里調用了prepare創建Looper對象。所以說,對于UI主線程而言,其Looper對象是由系統創建好的,用戶就無需自行創建了。  

    


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

忻州市| 东乌珠穆沁旗| 石城县| 仙游县| 嘉兴市| 灵川县| 南雄市| 资阳市| 增城市| 双牌县| 遂平县| 盐山县| 印江| 沈丘县| 吴川市| 运城市| 府谷县| 五莲县| 义乌市| 琼海市| 大同县| 崇仁县| 北川| 霸州市| 永春县| 都江堰市| 古田县| 庐江县| 华安县| 沂水县| 灌阳县| 沙洋县| 平南县| 天等县| 许昌市| 呼玛县| 洞口县| 海伦市| 广平县| 南汇区| 德格县|