Handler機(jī)制淺析

前言

Android應(yīng)用程序是基礎(chǔ)Java語(yǔ)言,內(nèi)部封裝了消息隊(duì)列,然后在主線程開(kāi)啟了死循環(huán)不斷獲取消息并處理來(lái)實(shí)現(xiàn)不間斷的界面變化與各種操作的執(zhí)行。這個(gè)過(guò)程主要由:Looper、Handler、Message三者來(lái)共同完成,Message作為消息載體,Looper用于封裝消息隊(duì)列,Handler用于插入與處理消息。

Looper簡(jiǎn)析

首先,看看Looper類的幾個(gè)成員變量

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;

sThreadLoca并非是一個(gè)線程的實(shí)現(xiàn)版本,它并不是一個(gè)Thread,而是線程局部變量。簡(jiǎn)單地說(shuō),它是一種較為特殊的線程綁定機(jī)制。很顯然,這里已經(jīng)暴露了Looper與線程之間肯定有一些“特殊的聯(lián)系”。

sMainLooper是個(gè)Looper對(duì)象,很顯然Looper不可能是單例實(shí)現(xiàn),并且從命名上我們也可以很容易看出來(lái)該對(duì)象,它可能是主線程/UI線程的Looper對(duì)象,咱們看到prepareMainLooper方法中初始化了這個(gè)對(duì)象

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

從方法注釋可以得知,該方法是實(shí)例化一個(gè)屬于該應(yīng)用主Looper(暫且這么理解),它是由系統(tǒng)自動(dòng)調(diào)用,不需要你手動(dòng)調(diào)用該方法

/**
 * Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

getMainLooper方法是提供獲取主線程Looper對(duì)象,也就是說(shuō)sMainLooper確實(shí)是屬于主線程的Looper對(duì)象,并且它在應(yīng)用開(kāi)始時(shí)就被初始化了
具體的初始化位置于ActivityThread#main()方法中,有興趣可自行了解

mQueue 是一個(gè)MessageQueue對(duì)象,咱們來(lái)看看官方對(duì)它的解釋

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 * 
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */
public final class MessageQueue {...}

我粗俗的翻譯下,MessageQueue是一個(gè)低級(jí)類,通過(guò)Looper提供消息集合的派發(fā),消息不會(huì)直接被添加到MessageQueue,而是通過(guò)Handler對(duì)象與其關(guān)聯(lián)Looper對(duì)象??偨Y(jié)下,就是MessageQueue的作用只是簡(jiǎn)單是存儲(chǔ)消息,具體的消息存取是由Handler與Looper來(lái)觸發(fā)與管理。

之前咱們提到,Looper很可能與線程間存在密切聯(lián)系。mThread成員變量再次證實(shí)了咱們的想法,怎么說(shuō)?不急,咱們現(xiàn)在先分析下Looper中的核心方法

首先,prepare方法

 /** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
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));
}

初始化生成當(dāng)前線程的Looper對(duì)象,只要當(dāng)prepare方法執(zhí)行了之后對(duì)應(yīng)線程中才可以創(chuàng)建Handler對(duì)象,并且必須確保在loop執(zhí)行前調(diào)用。從代碼中可以看出來(lái),此時(shí)sThreadLocal對(duì)象被實(shí)例化并與當(dāng)前線程綁定,What?你是想說(shuō)sThreadLocal.set(new Looper(quitAllowed));就完成了綁定?從哪得出線程信息了?好,我們?cè)賮?lái)看看構(gòu)造器。

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

OK,mThread = Thread.currentThread(),mThread現(xiàn)身了,引用了當(dāng)前線程

loop方法

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the 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;

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

注釋已經(jīng)給了明確的意思,它是開(kāi)始循環(huán)派發(fā)當(dāng)前線程的消息隊(duì)列,可以調(diào)用quit停止。咱們?cè)倏纯捶椒▋?nèi)部細(xì)節(jié),在開(kāi)始真正之前調(diào)用myLooper方法獲取了sThreadLocal.get()判斷若為空,表示當(dāng)前線程并沒(méi)有實(shí)例化過(guò)Looper,即并沒(méi)有調(diào)用prepare方法,于是拋出異常。

Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
...
for (;;) {
...
final long newIdent = Binder.clearCallingIdentity();
}

這里涉及到IPC通信中的UID與PIC識(shí)別,存儲(chǔ)于IPCThreadState中的mCallingPid與mCallingUid,簡(jiǎn)單的解釋下。假設(shè)processA對(duì)processB進(jìn)行了遠(yuǎn)程調(diào)用,processB必須攜帶processA的pid與uid,用于A端的權(quán)限校驗(yàn)。因此上述代碼塊的意思很明了:在每次派發(fā)消息前清除IPC身份標(biāo)識(shí),并判斷身份是否變化

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

獲取隊(duì)列中的下一個(gè)消息,獲取Looper的日志輸出對(duì)象。OK,開(kāi)始派發(fā)消息,在處理消息的前后輸出日志

需要注意的,loop方法中若成功執(zhí)行,那里面執(zhí)行for死循環(huán),將會(huì)阻塞后續(xù)代碼。即在loop調(diào)用后,后續(xù)的代碼將永遠(yuǎn)執(zhí)行不到,不信你看~

ActivityThread#main(String[] args)

public static void main(String[] args) {
...
    Looper.prepareMainLooper();
...
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Message淺析

Message,消息載體。包括:身份標(biāo)識(shí)what、數(shù)值變量arg1與arg2(用于額外數(shù)據(jù)較簡(jiǎn)單的情況下使用)、數(shù)據(jù)obj(Object類型)、replyTo(用于IPC,與本篇內(nèi)容)、數(shù)據(jù)data(Bunlde類型)、target對(duì)象(存儲(chǔ)目標(biāo)Handler對(duì)象)

/**
 * 
 * Defines a message containing a description and arbitrary data object that can be
 * sent to a {@link Handler}.  This object contains two extra int fields and an
 * extra object field that allow you to not do allocations in many cases.  
 *
 * <p class="note">While the constructor of Message is public, the best way to get
 * one of these is to call {@link #obtain Message.obtain()} or one of the
 * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
 * them from a pool of recycled objects.</p>
 */
 public final class Message implements Parcelable {...}

obtain方法

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    ...
}
/**
 * Same as {@link #obtain()}, but copies the values of an existing
 * message (including its target) into the new one.
 * @param orig Original message to copy.
 * @return A Message object from the global pool.
 */
public static Message obtain(Message orig) {
    ...
}
/**
 * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
 * @param h  Handler to assign to the returned Message object's <em>target</em> member.
 * @return A Message object from the global pool.
 */
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

實(shí)現(xiàn)Message的復(fù)用,內(nèi)部緩存是用一個(gè)鏈表形式的全局緩存池實(shí)現(xiàn),一共有八個(gè)重載方法。各個(gè)方法的差異只是對(duì)于獲取到的Message對(duì)象各信息賦值。

既然使用了緩存,那么肯定有回收消息的方法。對(duì)于被回收了消息,清除了對(duì)象信息后,若鏈表緩存池未滿,會(huì)把該消息放到鏈表的首部。結(jié)合recycleUnchecked與obtain方法可以看出,這是個(gè)后進(jìn)先出的鏈表。

public void recycle() {
    // check ...
    recycleUnchecked();
}
/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

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

總結(jié),Message對(duì)象作為消息內(nèi)容的載體,它的主要核心就是實(shí)現(xiàn)了鏈表緩存與對(duì)于消息內(nèi)容的存取與維護(hù)

Handler淺析

Handler,簡(jiǎn)單總結(jié)下源碼注釋
1.Handler用于處理與其自身關(guān)聯(lián)的線程中的消息隊(duì)列
2.一個(gè)Handler只對(duì)應(yīng)一個(gè)Looper與一個(gè)線程
3.Handler的主要兩個(gè)作用:消息的調(diào)度與處理;在不同線程間執(zhí)行操作

/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
 * 
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 * 
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 * 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 */
public class Handler {...}

咱們主要看兩方面內(nèi)容
1.Handler與Looper之間的關(guān)聯(lián)
2.Handler與Message之間的關(guān)聯(lián)

/**
 * Use the {@link Looper} for the current thread with the specified callback interface
 * and set whether the handler should be asynchronous.
 *
 * Handlers are synchronous by default unless this constructor is used to make
 * one that is strictly asynchronous.
 *
 * Asynchronous messages represent interrupts or events that do not require global ordering
 * with represent to synchronous messages.  Asynchronous messages are not subject to
 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
 *
 * @param callback The callback interface in which to handle messages, or null.
 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
 *
 * @hide
 */
public Handler(Callback callback, boolean async) {
    ...
    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;
}

在Handler實(shí)例化的時(shí)候,判斷了當(dāng)前線程對(duì)應(yīng)的Looper對(duì)象是否已創(chuàng)建。這也證明了在分析Looper時(shí)對(duì)Looper與線程之間存在聯(lián)系的判斷,以及Looper與線程是一一對(duì)應(yīng)的。

public final Message obtainMessage(...)
{
    ...
}

obtainMessage的四個(gè)重載方法,分別調(diào)用了相關(guān)的Message.obtain(…)

發(fā)送消息的兩種方式:Message與Runnbale,這里我就拿delayed舉例

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

從上面的代碼可以看出,發(fā)送Runnable實(shí)質(zhì)上也是Message對(duì)象,只不過(guò)Runnable方式會(huì)為Message對(duì)象的callback對(duì)象賦值

消息處理

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

消息處理時(shí)首先會(huì)判斷callback對(duì)象,若不為空則執(zhí)行對(duì)應(yīng)的回調(diào)代碼。否則將空方法handleMessage進(jìn)行處理,如果想要接收到消息后進(jìn)行對(duì)應(yīng)的事件操作,那么handleMessage是子類必須重寫的方法。

三者關(guān)系

用一張圖來(lái)解釋他們?nèi)咧g的聯(lián)系

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

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

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