2025-05-24

Windows編程的朋友可能知道Windows程序是消息驅動的,並且有全局的消息循環系統。而Android應用程序也是消息驅動的,按道理來說也應該提供消息循環機制。Android通過Looper、Handler來實現消息循環機制,Android消息循環是針對線程的(每個線程都可以有自己的消息隊列和消息循環)。

在 Android 系統 ,這些工作由由由Looper 及 Handler 來完成。

先分析Looper類:
主要提供負責線程的消息循環和消息隊列

    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();  // 進入消息循環
        }
    }

下面Looper類的準備函數:
private static final ThreadLocal sThreadLocal = new ThreadLocal();

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

這裡利用 ThreadLocal 線程局部存儲變量將將Looper與調用線程關聯起來。
而消息處理流程如何呢?
    /**
     *  Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static final void loop() {
        Looper me = myLooper();  // 線程中存儲的Looper對象
        MessageQueue queue = me.mQueue;  // Looper類中的消息隊列
        while (true) {
            Message msg = queue.next(); // might block   獲取消息
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);  // 利用 Target 註冊的方法處理消息
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                msg.recycle();
            }
        }
    }
   
可以通過Loop.myLooper()得到當前線程的Looper對象,通過Loop.getMainLooper()可以獲得當前進程的主線程的Looper對象。

在android系統,UI線程就是消息的主線程,在 ActivityThread.java 中創建:
public static final void main(String[] args) {
     Looper.prepareMainLooper();
    
     Looper.loop(); //消息循環處理
}

–>
    public static final void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        if (Process.supportsProcesses()) {
            myLooper().mQueue.mQuitAllowed = false;
        }
    }

    private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }
   
再來分析 Handler 類:
主要提供負責將消息加入到特定的Looper消息隊列中,並分發和處理訪消息隊列中的消息,構造Handler的時候可以指定一個Looper對象,如果不指定則利用當前線程的Looper創建。www.aiwalls.com

請看如下代碼即可明白:
    public Handler(Looper looper, Callback callback) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
    }
   
這裡也同時告訴我們將消息放入到主線程的消息隊列中,隻需要創建Hanlde對象時以主線程的Looper創建即可。則sendMessage及handleMessage都會在主線程中進行處理。

再看如下變量:
    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;

這裡也有 隊列,Looper對象及回調函數類,通過 Handler 不同構造函數完成相應的操作。
簡化使用隊列及消息傳遞的復雜性,提供方便的調用方法。其中最重要的函數是:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);  // 1、利用 Calback 函數處理消息
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) { // 2、利用mCallback處理消息
                    return;
                }
            }
            handleMessage(msg); // 3、利用子類處理消息,這裡最常用的方法,直接重載handleMessage函數
        }
    }
   
那麼這幾者的關系是怎麼樣的呢?
  

 

一個Activity中可以創建多個工作線程或者其他的組件,如果這些線程或者組件把他們的消息放入Activity的主線程消息隊列,那麼該消息就會在主線程中處理瞭。

還有一個問題就是Looper和Handler的同步關系如何處理,在android由HandlerThread類進行解決瞭。

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
       
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait(); // 如果新線程還未創建Looper對象,則等待
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

新線程創建運行run函數創建Looper對象:
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();// 這裡會創建程的Looper對象
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); // ok,創建好瞭則通知,最後調用Looper.loop()進入消息循環
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

這裡利用 notifyAll / wait 輕檢解決此問題瞭,所以多多使用用HanlderThread類完成多線程同步問題吧。

 摘自  andyhuabing的專欄 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *