Android 訊息處理 -- Looper 、Handler類

來源:互聯網
上載者:User

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.2cto.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的專欄 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.