天天看點

Android handler機制流程詳解

Android handler機制流程詳解

handler機制由以下幾部分組成:

.Handler
.Message
.MessageQueue
.Looper
           

總體流程介紹:

一、程序啟動時

在main方法中為主線程建立了對應自己線程的Looper,在Looper被建立的同時會建立一個MessageQueue消息隊列并持有。同時在main方法中調用了Looper.loop()進行一個死循環周遊Looper自己持有的消息隊列。

二、建立handler

如果在主線程中建立,handler能擷取到主線程對應的looper并持有。如果是在子線程中建立handler,則需要自己調用建立looper方法和自己調用Looper.loop。否則會報錯。

三、建立Message并發送

建立的Message将建立自己的handler資訊封裝進Message内部,最終被發送到了MessageQueue消息隊列中。

四、接收消息

在Looper.loop中,從MessageQueue中拿出Message,并根據其中對應的handler資訊進行分發處理

對應關系:

一條線程Thread對應唯一的一個Looper,其對應相關的資訊存儲在ThreadLocal中。

一個Looper對象持有一個自己的MessageQueue。

在同一條線程中,無論建立多少handler,都隻對應一個Looper和一個MessageQueue。

是以,handler在被建立時就根據Looper與對應的線程進行了綁定,即使在别的線程根據該handler發送了消息,也會被發送到handler對應的Looper的MessageQueue消息隊列中并被分發處理。

代碼分析:

接下來,舉一個簡單的例子,并且簡單分析一下主流程對應的原碼:

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
               
            }
        }
    };
    new Thread() {
            public void run() {
                    Message message = handler.obtainMessage();
                    message.what = 100;
                    message.obj = "測試+ : " + i;
                    handler.sendMessage(message);
            }
        }.start();
           

解析:

Handler:負責發送消息和接收傳回的消息,主流程部分源碼分析

public class Handler {
    //主要變量
    final Looper mLooper;//與每個線程對應的looper
    final MessageQueue mQueue;//消息隊列
    final Callback mCallback;//是否同步

    //構造方法
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            ..
        }
        mLooper = Looper.myLooper();//根據目前線程資訊在ThreadLocal中擷取線程對應的looper
        注:
        .如果是在子線程中建立,需要調用Looper.prepare( boolean quitAllowed)
        .該方法的作用是擷取調用方法的線程資訊,判斷目前線程是否有looper,沒有則建立。
        .在ActivityThread中建立過主線程的looper

        if (mLooper == null) {
            ..
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }


//從消息對象池中取出消息
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }


//發送消息的各種方法
handler.sendMessage(message);
handler.sendMessageDelayed(
    Message msg, long delayMillis);
handler.sendMessageAtTime(
    Message msg, long uptimeMillis);
handler.sendEmptyMessage(
    int what);
handler.sendEmptyMessageDelayed(
    int what, long delayMillis);
handler.sendMessageAtFrontOfQueue(
    Message msg);
handler.sendEmptyMessageAtTime(
    int what, long uptimeMillis);

    //最終都是調用來發送
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//将目前handler資訊封裝進Message
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }


//分發消息

    /**
     * 該方法在Looper中next()方法中被調用  根據msg.target來區分
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

}
           

主要發生的大事:

一、當調用:new Handler() 時:

1、Handler中:Looper.myLooper();

在ThreadLocal中擷取目前線程對應的looper,主線程對應的looper在main方法中已經被建立

2、mQueue = mLooper.mQueue;

mCallback = callback;

mAsynchronous = async;

取出looper中的消息隊列(mQueue)。Callback對象。是否同步。封裝進該handler對象

二、當調用:handler.obtainMessage()時:

在消息對象池中擷取消息。

三、當調用:sendMessage或者各種send方法時:

最終調用 enqueueMessage,将目前handler對象封裝進message.target,

并調用mQueue.enqueueMessage(msg, uptimeMillis),将消息發送給消息隊列

Message:負責封裝消息,主流程部分源碼分析

public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    long when;
    Bundle data;
    Handler target;
    Runnable callback;
    Message next;
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    private static boolean gCheckRecycle = true;


    //被handler調用
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

    /**
     * 判斷消息對象池中是否有消息,并從消息對象池中擷取消息
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    省略其他好多方法...
           

結論:在handler中使用obtain(this)來擷取消息池中消息對象時,将目前this封裝進message.target中

Looper:不斷循環,從消息隊列中擷取消息,并根據消息的target來進行消息分發,即調用對應handler的dispatchMessage方法。主流程部分源碼分析

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // 主線程looper對象
    final MessageQueue mQueue; //消息隊列
    final Thread mThread;//目前線程

    //構造方法:隻有一個構造,并且建立mQueue 和給mThread 指派
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    //初始化,如果handler在子線程中被調用,則要先調用此方法
    public static void prepare() {
        prepare(true);
    }

    //被上面方法調用
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //該方法内部根據目前線程資訊判斷是否建立了looper對象,如果有建立,則傳回對應looper對象。
            //如果沒有建立,則傳回空
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //建立一個looper對象,并将目前線程和該looper綁定
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //主線程在建立的時候在ActivityThread類中main方法中被調用
     建立主線程對應的looper和給目前sMainLooper指派
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    //handler中擷取looper的方法
    public static @Nullable
    Looper myLooper() {
        //該方法内部根據目前線程資訊判斷是否建立了looper對象,如果有建立,則傳回對應looper對象。
        //如果沒有建立,則傳回空
        return sThreadLocal.get();
    }

    //開啟loop循環查詢隊列,
    主線程對應的loop方法在main中被調用,如果在子線程中建立的handler,需要自己調用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;

        省略衆多代碼....

        for (; ; ) {
        
        //隊列的next()方法中處理基本邏輯,如果有消息:傳回。如果沒有消息:阻塞||等待
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            省略衆多代碼.....
            try {
                msg.target.dispatchMessage(msg);//根據targer(即handler)進行分發
                ..
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //将消息放回消息池
            msg.recycleUnchecked();
        }
    }
}
           

該類負責做的事情:

1、在prepare()被調用時:

該方法可以定義為:建立調用線程對應的looper,判斷被調用的線程是否有looper對象,如果有就提示錯誤,沒有則進行建立并綁定線程和looper。

2、構造被調用時:

建立 MessageQueue 消息隊列,和給mThread變量(目前線程)指派

3、在myLooper()被調用時:

該方法可以定義為擷取線程對應的looper,判斷被調用的線程是否有looper對象,如果有就傳回,沒有傳回空。

4、prepareMainLooper()方法在main方法中被調用,建立主線程對應的looper。

5、loop()方法在main中被調用,作用為死循環從目前線程對應looper持有的消息隊列中取出消息,并根據消息對應的targer進行分發。

注:當源碼解析到這裡有一個疑問:為什麼在主線程裡面Looper的死循環不會引發ANR

解釋:Looper.loop無限循環為什麼沒有引發ANR

MessageQueue:消息隊列,接收handler發送過來的消息存入隊列,死循環周遊消息隊列傳回給looper,消息 隊列沒有消息時,阻塞||等待。主流程部分源碼分析

public final class MessageQueue {

    private final boolean mQuitAllowed;
    是否允許退出
    private boolean mQuitting;//是否放棄消息隊列

    @SuppressWarnings("unused")
    private long mPtr;

    private native static long nativeInit();//傳回一個NativeMessageQueue,具體實作過程在Nativity層

    private native static void nativeWake(long ptr);//喚醒對應線程


    //構造
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;//是否允許退出
        mPtr = nativeInit();//傳回一個NativeMessageQueue,具體實作過程在Nativity層
    }


    //該方法就是在handler中調用send系列方法最終調用的方法,将消息放入消息隊列
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//msg.target就是發送此消息的Handler
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {//表示此消息正在被使用
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {//表示此消息隊列被放棄
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;//将延遲時間封裝到msg内部
            Message p = mMessages;//消息隊列的第一個元素
            boolean needWake;//是否需要喚醒線程
            if (p == null || when == 0 || when < p.when) {
//如果此隊列中頭部元素是null(空的隊列,一般是第一次),
或者此消息不是延時的消息,則此消息需要被立即處理,
此時會将這個消息作為新的頭部元素,并将此消息的next指向舊的頭部元素,
然後判斷如果Looper擷取消息的線程如果是阻塞狀态則喚醒它,讓它立刻去拿消息處理。

                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
//如果此消息是延時的消息,則将其添加到隊列中,
原理就是連結清單的添加新元素,按照when,也就是延遲的時間來插入的,
延遲的時間越長,越靠後,這樣就得到一條有序的延時消息連結清單,
取出消息的時候,延遲時間越小的,就被先擷取了。插入延時消息不需要喚醒Looper線程

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

            if (needWake) {
                nativeWake(mPtr);//喚醒線程
            }
        }
        return true;
    }


    //該方法就是在Looper中被調用的方法,傳回一個message,如果隊列為空,則阻塞
    Message next() {
        final long ptr = mPtr;
//從注釋可以看出,隻有looper被放棄的時候(調用了quit方法)才傳回null,
mPtr是MessageQueue的一個long型成員變量,關聯的是一個在C++層的MessageQueue,
阻塞操作就是通過底層的這個MessageQueue來操作的;當隊列被放棄的時候其變為0
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1;
        int nextPollTimeoutMillis = 0;
        for (; ; ) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

//阻塞方法,主要是通過native層的epoll監聽檔案描述符的寫入事件來實作的。
//如果nextPollTimeoutMillis=-1,一直阻塞不會逾時。
//如果nextPollTimeoutMillis=0,不會阻塞,立即傳回。
//如果nextPollTimeoutMillis>0,最長阻塞nextPollTimeoutMillis毫秒(逾時),如果期間有程式喚醒會立即傳回。
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
//msg.target == null表示此消息為消息屏障(通過postSyncBarrier方法發送來的)
//如果發現了一個消息屏障,會循環找出第一個異步消息(如果有異步消息的話),所有同步消息都将忽略(平常發送的一般都是同步消息)  
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
// 如果消息此刻還沒有到時間,設定一下阻塞時間nextPollTimeoutMillis,進入下次循環的時候會調用
//nativePollOnce(ptr, nextPollTimeoutMillis)進行阻塞;

                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //正常取出消息
                        //設定mBlocked = false代表目前沒有阻塞
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //沒有消息,會一直阻塞,直到被喚醒
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

}
           

主要做的事:

MessageQueue() 構造:在Looper建立時被建立

enqueueMessage():handler一系列send方法最終殊途同歸到這個方法,将消息放入消息隊列

next():在Looper.loop() 方法中被調用,周遊隊列并傳回消息,如果隊列中沒有消息則阻塞