【Android 消息處理】Handler、Looper、Message 源碼淺析

Android 消息處理機制核心的四個類是 Handler、Looper、Message 和 MessageQueue

Message

屬性

Message 類的幾個成員變量

//前面四個字段是 public 的,可以直接修改屬性值,Bundle 對象需要使用 set 和 get 方法。
 public int what;    
 public int arg1;  
 public int arg2;
 public Object obj;
 Bundle data;

 long when;
 Handler target;
 Runnable callback;
 Message next;

 private static final Object sPoolSync = new Object();
 private static Message sPool;
 private static int sPoolSize = 0;

創(chuàng)建對象

創(chuàng)建一個 Message 對象可以通過無參的構(gòu)造方法,但是推薦使用 Message 類提供的 static 的 obtain 方法。

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

因為 Message 內(nèi)部維護了一個 Message 的對象池,上面提到的 sPoolSync 字段主要是同步用的,保證多線程安全,如果對象池不為空,那么棧頂?shù)?Message 就被取出重用,指針指向下一個 Message 對象,隊列長度減1。
如果對象 池為空,就 new 一個新的 Message 對象返回。
Message 有個 recycle 方法,每次 Looper 輪詢完都會調(diào)用 Message 的該方法進行回收,該方法內(nèi)部調(diào)用了 recycleUnchecked 方法,把 Message 的所有屬性都還原,并入棧。
Message 還重載了很多帶參數(shù)的 obtain 的方法,這些方法都先調(diào)用了無參的 obtain 方法獲取一個 Message 對象,然后對屬性進行賦值:

public static Message obtain(Message orig)
public static Message obtain(Handler h) 
public static Message obtain(Handler h, Runnable callback)//callback 字段沒有提供 set 方法,只能在創(chuàng)建對象時傳入,callback 并不是一個回調(diào)接口,而是一個 Runnable 對象
public static Message obtain(Handler h, int what)
public static Message obtain(Handler h, int what, Object obj)
public static Message obtain(Handler h, int what, int arg1, int arg2)
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)

我們平時發(fā)送 Message 都是通過 Handler 對象的相關(guān)方法,也可以調(diào)用 Message 自身的 sendToTarget 方法,當然前提是在創(chuàng)建 Message 時傳入了 Handler 參數(shù)或者通過 setTarget 方法為 target 屬性賦值,該內(nèi)部也是調(diào)用了 Handler 的 sendMessage 方法:

public void sendToTarget() {
        target.sendMessage(this);
    }

Handler

Handler 也重載了構(gòu)造方法,這些構(gòu)造方法可以分為兩類,一類是傳入了 Looper 對象的,一類是沒有傳入 Looper 對象的。

public Handler()
public Handler(Callback callback)
public Handler(boolean async)
public Handler(Callback callback, boolean async) 

public Handler(Looper looper)
public Handler(Looper looper, Callback callback)
public Handler(Looper looper, Callback callback, boolean async)

傳入 Looper 對象的最后都調(diào)用了三個參數(shù)的構(gòu)造方法,在構(gòu)造方法中對 mLooper、mQueue、mCallback、mAsynchronous 成員變量進行了初始化,mCallback 可以為 null,mAsynchronous 默認為 false:

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

沒有傳入 Looper 對象的最后都調(diào)用了兩個參數(shù)的構(gòu)造方法,

 public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在該構(gòu)造函數(shù)中通過 Looper.myLooper() 方法獲取到一個 Looper 對象對 mLooper 屬性進行賦值,也就是說在 Handler 的構(gòu)造函數(shù)中必須綁定一個 Looper 對象,否則會拋異常。Looper.myLooper() 方法獲得的是當前線程的 Looper 對象,也就是創(chuàng)建 Handler 的代碼所在的線程,所以在使用沒有傳入 Looper 對象的構(gòu)造函數(shù)構(gòu)造 Handler 時,當前線程必須有一個 Looper 對象,在 UI 線程中已經(jīng)創(chuàng)建了 Looper 對象,而在子線程中默認情況下是沒有 Looper 對象的。如果通過構(gòu)造函數(shù)傳入 Looper 的話則可以是任意線程的 Looper。
Handler 內(nèi)部也提供了創(chuàng)建了 Message 的構(gòu)造方法,內(nèi)部其實也是調(diào)用了 Message 的 obtain 方法,Message 對象的 target 屬性就是該 Handle 對象:

public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what, Object obj)
public final Message obtainMessage(int what, int arg1, int arg2)
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)

Handler 發(fā)送消息主要有 sendXXX 和 postXXX 兩個系列,先來看 send 開頭的方法:

public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessage(Message msg)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

sendEmptyMessageXXX 就是內(nèi)部創(chuàng)建一個只有 what 屬性的 Message 對象,這些方法最后都是調(diào)用了 sendMessageAtTime 方法:

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

方法內(nèi)部就是將 Message 對象加入在構(gòu)造函數(shù)中綁定的 MessageQueue,同時將自身賦值給 Message 的 target 字段,最終由這個 Handler 來處理該 Message。
post 開頭的方法最終也是調(diào)用了 sendMessageAtTime 方法,不同之處是傳入了一個 Runnable 對象

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)

postXXX 方法內(nèi)部調(diào)用了 getPostMessage 方法創(chuàng)建了一個 Message 對象,傳入的 Runnable 類型的參數(shù)就是賦值給這個 Message 對象的 callback 字段。所以使用 postXXX 方法時,在 Handler 所在的線程要執(zhí)行代碼直接寫在 Runnable 的 run 方法里面,不需要在重寫 Handler 的 handleMessage 方法。

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

Handler 在接收到 Messgae 后會調(diào)用 dispatchMessage 方法

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

可以看到,如果 Message 的 callback 屬性不為 null,則執(zhí)行 Message 的 callback 屬性的 run 方法,也就是我們在創(chuàng)建 Message 對象時傳入的 Runnable 參數(shù)或者 postXXX 方法中傳入的 Runnable 參數(shù);
如果 callback 為 null,則調(diào)用 Handler 的 mCallback 的 handleMessage 方法,這個 mCallback 也是創(chuàng)建 Handler 對象時傳入的參數(shù),只是這個 mCallback 不是 Runnable 對象,是一個Handler 內(nèi)部聲明的回調(diào)接口 Callback。
如果 mCallback 也為 null 的話,則調(diào)用 handleMessasge 方法,這個方法是在定義 Handler 時重寫的方法。

Looper

前面說過,通過 Looper.myLooper 方法可以獲得當前線程的 Looper 對象

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

這里的 ThreadLocal 成員變量使每一個線程保持一個各自獨立的對象,為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本。
在非 UI 線程中并沒有 Looper 對象,需要調(diào)用 prepare 方法,創(chuàng)建一個 Looper 對象,這樣 myLooper 方法就是 null le。

public static void prepare() {
        prepare(true);
    }

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

可以看到 prepare 方法就是 new 了一個 Looper 對象然后存儲到 ThreadLocal 中,每個線程只能創(chuàng)建一個 Looper 對象。

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

在 Looper 的構(gòu)造方法里面就是創(chuàng)建了一個 MessageQueue 成員變量,然后將當前線程(Looper.prepare 方法執(zhí)行的線程)賦值給 mThread 成員變量,把 Looper 和當前 Thread 綁定。
有了 Looper 對象就可以不斷地輪詢消息隊列處理消息了

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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

在 loop 方法里面分別先獲取當前線程(loop 方法執(zhí)行的線程)的 Looper 對象,然后得到該 Looper 對象的 MessageQueue,從 MessageQueue 中取出隊首的 Message,調(diào)用該 Message 目標 Handler 的 diapatchMesssage 方法進行消息處理,在消息處理完畢后對 Message 對象進行了回收。
Looper 也提供了獲取主線程 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();
        }
    }

MessageQueue

MessageQueue 主要的兩個方法 enqueueMessage 和 next 分別在 Handler 和 Looper 中調(diào)用,這里不作討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容