android 訊息系統Handler、MessageQueue、Looper原始碼學習

來源:互聯網
上載者:User

標籤:callback   指標   串列   long   messages   ide   track   綁定   create   

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原始碼學習

相關文章

聯繫我們

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