1. 程式人生 > >android 消息系統Handler、MessageQueue、Looper源代碼學習

android 消息系統Handler、MessageQueue、Looper源代碼學習

red css llb patch ... handle eas 除了 pro

android消息系統

總體框架如圖所看到的
技術分享圖片
在安卓的消息系統中,每一個線程有一個Looper,Looper中有一個MessageQueue,Handler向這個隊列中投遞Message,Looper循環拿出Message再交由Handler處理。總體是一個生產者消費者模式,這四部分也就構成了android的消息系統。


先來看一個最簡單的樣例

        //這段代碼在某個Activity的onCreate中
        Handler handler = new Handler
(Looper.getMainLooper());
Message msg = Message.obtain(handler, new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext,"I am a message",Toast.LENGTH_SHORT).show(); } }); handler.sendMessage(msg);

效果就是。在當前窗體彈出I am a message。當然就事實上現的效果而言全然多此一舉。可是就分析android消息系統。卻是非常easy有效的樣例。


源代碼分析

Message

Message中封裝了我們經常使用的what、arg1、arg2、obj等參數,除此之外還有target:一個Handler類型,由前文可知一個Message終於還是交給一個Handler運行的。這個target存放的就是消息的目的地、callback。一個消息的回調。我們通過handler.post(new Runnable{…})發送的消息。這個Runnable即被存為callback。
首先來看消息的獲取:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

對比最開始的樣例,Message.obtain(Handler h, Runnable callback)首先調用obtain獲取了一個新的Message對象。然後為其設置了目的地Handler和回調函數callback。Message類中有非常多不同的obtain函數。實際上僅僅是為我們封裝了一些賦值的操作。

再看Message.obtain()方法。sPoolSync是一個給靜態方法用的靜態鎖。sPool是一個靜態的Message變量。在消息的獲取這裏,android使用了享元模式,對於會被反復使用的Message消息。沒有對每一次請求都新建一個對象。而是通過維護一個Message鏈表,在有空暇消息的時候從鏈表中拿Message。沒有時才新建Message。
能夠看到obtain中僅僅有從鏈表中去Message和新建Message。而沒有向鏈表中存儲的過程。

存儲這部分就要看Message.recycle()了:

    public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

回收過程。首先把原鏈表的頭指向當前被回收消息的下一個節點。然後再把鏈表頭指針知道當前節點就可以。整個操作也就是將Message加入到鏈表的首位。


MessageQueue 消息隊列

MessageQueue是在Looper中的。這點從Looper的構造函數能夠看出來:

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

對於每一個MessageQueue,是鏈表實現的消息隊列。

首先是入隊操作:

boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {
            throw new AndroidRuntimeException("Message must have a target.");
        }

        synchronized (this) {
            if (mQuitting) {
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }

            msg.when = when;
            //mMessages是鏈表的頭指針
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // 將消息插入到隊列的首位
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    //當抵達隊列尾部、或者當前消息的時間小於隊列中某消息的時間跳出循環
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //將消息插入到鏈表中間(包括尾部)
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

next操作。包括取出和刪除一條消息。

Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            //從native層消息隊列取出消息
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 找到非異步的Message或者消息隊列尾部的Message取出
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 消息尚未到運行時間,下次循環掛起線程一段時間
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 獲取一個Message
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // 檢查退出標誌位
                if (mQuitting) {
                    dispose();
                    return null;
                }

                ...
        }
    }

Handler

Handler的作用是放入消息和處理消息,承擔了生產者的工作和部分消費者的工作。
首先通過Handler發送一條消息:


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

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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

通過一層一層嵌套,真正的邏輯在sendMessageAtTime,能夠看到僅僅是運行了一下入隊操作。作為生產者的工作也就運行完畢,消費者部分後面要結合Looper分析。
除了sendMessage方法,經常使用的handler.post方法也是封裝為Message,主要過程和上面類似。

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

Looper

Looper類中,Looper的實例獲取是通過ThreadLocal的。ThreadLocal會為每一個線程提供一個副本,通過set和get方法每一個線程獲取作用域僅屬於該線程的變量值。對於UI線程而言,會運行Looper.prepareMainLooper()來完畢Looper的初始化:


public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper.prepare()方法將當前線程的ThreadLocal設置了一個新的Looper對象。prepareMainLooper則是把當前線程的Looper對象賦值給類變量sMainLooper 。該方法在ActivityThread中調用,設置了一個全局的給UI線程使用的Looper。

Looper的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;


        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        //從Looper中獲取MessageQueue,循環取出消息
        for (;;) {
            Message msg = queue.next();

            ...

            //將消息發送給目標處理。
            msg.target.dispatchMessage(msg);

            ...

            //回收消息,把消息放在消息池中
            msg.recycle();
        }
    }

主要邏輯非常清晰,前面分析過msg.target是一個Handler,表示處理消息的目標,通過命令模式將消息交給相應Handler處理。

以下是Handler中處理消息的方法:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
private static void handleCallback(Message message) {
        message.callback.run();
    }
public void handleMessage(Message msg) {
    }

假設我們是通過handler.post的方法發送一條消息,那麽直接運行callback中的邏輯。

否則通過實現Callback接口回調,或者運行handleMessage,handleMessage也就是我們子類覆寫的方法。

能夠看到,盡管邏輯部分是我們在Handler中實現的。可是調用的地方卻是Looper的線程。由於一個Looper綁定一個線程,我們也能夠通過比較Looper來比較線程。


總結

通過分析源代碼,能夠知道android中能夠通過Looper為每一個線程創建一個消息隊裏,UI線程的Looper在Activity啟動前就已經初始化。

那麽對於我們自己定義的線程。非常明顯也能夠綁定Looper。
自己定義線程綁定Looper。最明顯的優點就是能夠實現線程間通信了,同一時候由於借助了消息隊列,也將並行轉為串行實現了線程安全。看一個簡單的樣例:

        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handlerA = new Handler(Looper.myLooper()){
                    @Override
                    public void handleMessage(Message msg) {
                        Log.d("TAG", msg.obj.toString());
                    }
                };
                Looper.loop();
            }
        }).start();

上述在線程中創建綁定了一個Looper,然後新建一個和當前Looper綁定的Handler,這樣能夠通過該Handler向Looper的MessageQueue中加入消息,然後由Looper.loop取出消息並運行。

        Message msg = new Message();
        msg.obj = "i am main thread";
        handlerA.sendMessage(msg);

在主線程或者其他線程中獲取handler然後發送消息,終於能夠看到消息被線程接收並處理。

這裏msg的target也就是handlerA。

註意假設線程工作結束,須要調用Looper.quit()。不然會由於Looper一直循環而導致線程無法結束。

最後經過上面的分析,流程圖能夠畫的更為仔細:

技術分享圖片

android 消息系統Handler、MessageQueue、Looper源代碼學習