Android Handler消息機(jī)制詳解

???????在Android中,只有主線程才能更新UI,但是主線程不能進(jìn)行耗時(shí)操作,否則會(huì)產(chǎn)生ANR異常,所以常常把耗時(shí)操作放到其他子線程進(jìn)行。如果在子線程中需要更新UI,一般都是通過Handler發(fā)送消息,主線接收消息后進(jìn)行相應(yīng)的UI邏輯處理。

一.什么是Handler

???????Handler是一個(gè)消息分發(fā)對(duì)象。
???????Handler是Android系統(tǒng)提供的一套用來更新UI的機(jī)制,也是一套消息處理機(jī)制,可以通過Handler發(fā)消息,也可以通過Handler處理消息。

二.為什么使用Handler

???????為了解決多線程并發(fā)的問題!
???????比如:如果在一個(gè)activity中有多個(gè)線程同時(shí)更新UI,并且沒有加鎖,就會(huì)出現(xiàn)界面錯(cuò)亂的問題。但是如果對(duì)這些更新UI的操作都加鎖處理,又會(huì)導(dǎo)致性能下降。出于對(duì)性能問題的考慮,Android提供這一套使用Handler更新UI的機(jī)制,不用再去關(guān)心多線程的問題,所有的更新UI的操作,都是在主線程的消息隊(duì)列中去輪詢處理的。
???????在Android系統(tǒng)中,只有主線程才能更新UI,提到主線程,就不得說一下ActivityThread,一個(gè)應(yīng)用內(nèi)部的邏輯處理都是在ActivityThread內(nèi)部依靠Handler來進(jìn)行處理的,比如:activity、service相關(guān)的創(chuàng)建等相關(guān)邏輯,在應(yīng)用創(chuàng)建后,會(huì)調(diào)用到ActivityThread內(nèi)部的main()方法,邏輯如下:

public static void main(String[] args) {
    ......
    //創(chuàng)建Looper
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    //創(chuàng)建Handler
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    //開啟loop()循環(huán)
    Looper.loop();
}

??????從上面可以看到在ActivityThread里面的main()中,執(zhí)行了Looper.prepareMainLooper()及Looper.loop(),接下來一起分析一下Android系統(tǒng)的消息處理機(jī)制。

三.源碼分析

???????Android內(nèi)部的消息處理機(jī)制主要是由Handler、Looper、MessageQueue、Message來組成的,具體分工如下:
???????Handler:負(fù)責(zé)發(fā)送消息及處理消息
???????Looper:不斷的從消息隊(duì)列中取出消息,并且將消息給發(fā)送本條消息的Handler
???????MessageQueue:負(fù)責(zé)存儲(chǔ)消息
???????Message:消息本身,負(fù)責(zé)攜帶數(shù)據(jù)

1.Looper

???????Looper分為主線程和其他子線程,前面講到,主線程的Looper是在進(jìn)程啟動(dòng)后調(diào)用ActivityThread的main()里面通過prepareMainLooper()創(chuàng)建的:

a.prepareMainLooper()

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

???????prepareMainLooper()內(nèi)部會(huì)調(diào)用prepare(false)來進(jìn)行創(chuàng)建,且Looper是不能退出的,然后對(duì)sMainLooper進(jìn)行賦值;

b.prepare()

//只能通過Looper.prepare()方法去初始化一個(gè)Looper
public static void prepare() {
    prepare(true);
}

//一個(gè)線程中只能有一個(gè)Looper對(duì)象,否則在第二次嘗試初始化Looper的時(shí)候,就會(huì)拋出異常
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));//創(chuàng)建了new Looper
}

???????子線程通過prepare()內(nèi)部調(diào)用prepare(true)來創(chuàng)建對(duì)應(yīng)的Looper,且Looper是可以退出的,為什么要退出,后面會(huì)講到;

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

private Looper(boolean quitAllowed) {
    //創(chuàng)建Looper的時(shí)候會(huì)創(chuàng)建一個(gè)MessageQueue
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

???????Looper構(gòu)造方法內(nèi)會(huì)創(chuàng)建MessageQueue(),為后續(xù)消息處理做準(zhǔn)備,然后獲取到當(dāng)前的Thread賦值給mThread,后續(xù)通過getThread()可以獲取到當(dāng)前的thread,可以用來判斷是否為主線程。

c.loop()

???????Looper在通過prepare(x)后需要執(zhí)行l(wèi)oop()來將消息進(jìn)行循環(huán)處理;

public static void loop() {
    final Looper me = myLooper();
    //如果Looper為null,則拋出異常
    //主線程啟動(dòng)時(shí)會(huì)創(chuàng)建Looper,所以主線程創(chuàng)建多個(gè)Handler時(shí),通過sThreadLocal.get()獲取的是同一個(gè)Looper。
    //如果非主線程創(chuàng)建Handler,需要先執(zhí)行Looper.prepare()來創(chuàng)建Looper,否則loop()時(shí)會(huì)拋異常
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
    //不斷的循環(huán)取消息,如果沒有消息則等待
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // message為空,說明調(diào)用了quit(),會(huì)return繼而退出loop()
            return;
        }

        try {
            //處理消息
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        .......
        //消息處理完后釋放
        msg.recycleUnchecked();
     }
}

???????可以看到,在loop()內(nèi)部會(huì)先通過myLooper()獲取到對(duì)應(yīng)線程的Looper,繼而獲取到對(duì)應(yīng)的MessageQueue,接下來不斷的去讀取MessageQueue內(nèi)部的消息,然后進(jìn)行處理;
???????沒有消息時(shí),會(huì)一直阻塞在Message msg = queue.next(),等待MessageQueue分發(fā)消息;
???????有消息時(shí),調(diào)用Handler來處理消息;
???????消息處理完后進(jìn)行recycleUnchecked()處理。

d.quitSafely()

???????子線程在沒有消息需要處理時(shí),需要退出looper,否則一直block在loop()中的Message msg = queue.next();

public void quitSafely() {
    mQueue.quit(true);
}

??????前面講到,子線程的Looper是可以退出的,通過調(diào)用以上方法來退出,其實(shí)最終調(diào)用的MessageQueue的quit(),消息隊(duì)列退出了,就沒有消息進(jìn)行處理了,Looper也就不需要輪詢了,也就可以退出了。

2.Handler

a.Handler()

//默認(rèn)主線程
public Handler() {
    this(null, false);
}
//使用線程已經(jīng)創(chuàng)建好的Looper
public Handler(Looper looper) {
    this(looper, null, false);
}

public Handler(Callback callback, boolean async) {
    ......
    //獲取上面主線程創(chuàng)建的Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //獲取上面主線程Looper里面創(chuàng)建的MessageQueue
    mQueue = mLooper.mQueue;
    //默認(rèn)為null,通過handler中的handleMessage來處理
    mCallback = callback;
    .......
}

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

???????通過以上可以看到,在Handler的構(gòu)造方法會(huì)分為幾類:
???????無參:代表默認(rèn)使用當(dāng)前線程的handler(一般是主線程),會(huì)通過Looper.myLooper()來獲取到對(duì)應(yīng)的Looper,如果Looper為null的話會(huì)拋異常,所以子線程使用Handler需要先通過Looper.prepare()來創(chuàng)建Looper;
???????傳入Looper參數(shù):直接使用傳入的Looper(使用HandlerThread時(shí)這樣操作);
???????從Looper中獲取到對(duì)應(yīng)的MessageQueue,Callback默認(rèn)為null,通過實(shí)現(xiàn)的handleMessage()來處理。
???????可以看到,在創(chuàng)建Handler時(shí)會(huì)通過myLooper()來獲取到對(duì)應(yīng)線程的Looper,此時(shí)兩者建立了聯(lián)系;

b.sendMessage()

???????當(dāng)調(diào)用Handler的sendMessagexx()發(fā)送消息時(shí),最終都會(huì)調(diào)用到sendMessageAtTime(),內(nèi)部就是將message加入到messagequeue中。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    //獲取MessgeQueue(即主線程的MessageQueue,上面已經(jīng)創(chuàng)建完成)
    MessageQueue queue = mQueue;
    if (queue == null) {
        return false;
    }
    //將message加入隊(duì)列
    return enqueueMessage(queue, msg, uptimeMillis);
}

??????調(diào)用post(Runnable)也會(huì)將runnable轉(zhuǎn)換為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;
}

??????通過post(Runnable r)方式,最終也會(huì)轉(zhuǎn)換為Message方式,將Runnable賦值給callback,有消息到來時(shí),調(diào)用run方法來UI更新邏輯。

c.enqueueMessage()

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

???????可以看到,在該方法內(nèi)部主要執(zhí)行了三件事:
???????1.將Handler對(duì)象賦值給msg.target,從而message持有Handler對(duì)象的引用;
???????2.如果mAsynchronous為true,會(huì)執(zhí)行setAsynchronous(true)將其設(shè)置為異步消息,跟消息屏障一起使用,處理優(yōu)先級(jí)高的消息(UI繪制);
???????3.調(diào)用MessageQueue內(nèi)的enqueueMessage()最終把發(fā)送的Message放進(jìn)了主線程的MessageQueue里面進(jìn)行循環(huán);

d.dispatchMessage()

???????前面講到,Looper在loop()時(shí),從MessageQueue中取到消息時(shí),會(huì)通過msg.target.dispatchMessage()來通知Handler來進(jìn)行處理;

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

???????可以看到,在該方法內(nèi)部會(huì)進(jìn)行順序判斷來執(zhí)行不同的消息處理邏輯:
???????1.callback是通過post(Runnable)方式中的Runnable[下面有解釋],通過sendMessage時(shí)為null;
???????2.mCallback默認(rèn)為null;
???????3.調(diào)用子類的handleMessage方法,最終會(huì)調(diào)用本地實(shí)現(xiàn)的Handler中的handleMessage()來進(jìn)行處理。

3.MessageQueue

???????MessageQueue的主要方法有enqueueMessage():將消息添加到queue里;next():不斷的取出message;quit():退出消息循環(huán),繼而退出Looper.loop()。

a.enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
    ......
    //----------------分析點(diǎn)1------------------------
    synchronized (this) {
        ......
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //----------------分析點(diǎn)2------------------------
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            //-------------------分析點(diǎn)3---------------------------
            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.
        //-------------------分析點(diǎn)4---------------------------
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

??????從上面代碼可以看到,主要的點(diǎn)為:
??????分析點(diǎn)1:加入synchronized (this)是防止多個(gè)Handler同時(shí)往queue中插入消息,出現(xiàn)混亂的場景。
??????分析點(diǎn)2:消息的queue中沒有消息或message需要立刻執(zhí)行(when=0)或當(dāng)前message執(zhí)行時(shí)間小于queue里面所有消息的最早執(zhí)行時(shí)間時(shí),就把當(dāng)前message插于queue的頭部,然后執(zhí)行nativeWake()喚醒next(),然后將message最早給loop()執(zhí)行;
??????分析點(diǎn)3:當(dāng)不滿足以上3個(gè)條件時(shí),則需要把當(dāng)前消息根據(jù)執(zhí)行時(shí)間(when)插入到queue中的合適位置,此時(shí)不需要執(zhí)行nativeWake()喚醒next(),因?yàn)椴粓?zhí)行當(dāng)前消息;
??????分析點(diǎn)4:是否需要喚醒next(),來按順序執(zhí)行消息;

b.quit()

void quit(boolean safe) {
    //主線程的MessageQueue不允許退出
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    //加入同步鎖,如果正在退出則直接返回
    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        //喚醒鎖
        nativeWake(mPtr);
    }
}

??????當(dāng)執(zhí)行Looper.quit()后,實(shí)際執(zhí)行的是Message.quit(),在quit()里面設(shè)置mQuitting=true,最后執(zhí)行nativeWake(mPtr)來喚醒next(),最終返回message為null,然后Looper.loop()里面檢測到獲取到的message為null,直接退出loop()。

c.next()

Message next() {
    .........
    for (;;) {
        //-------------------分析點(diǎn)1---------------------------
        nativePollOnce(ptr, nextPollTimeoutMillis);
        //-------------------分析點(diǎn)2---------------------------
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //-------------------分析點(diǎn)3---------------------------
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //-------------------分析點(diǎn)4---------------------------
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    //-------------------分析點(diǎn)5---------------------------
                    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 {
                //queue里面沒消息時(shí),就將nextPollTimeoutMillis置為-1,一直等待中
                nextPollTimeoutMillis = -1;
            }
        // Process the quit message now that all pending messages have been handled.
        //當(dāng)執(zhí)行quit()時(shí),會(huì)執(zhí)行以下邏輯
        if (mQuitting) {
            dispose();
            //返回的message為null,Looper.loop()中的Message msg = queue.next()接收到message為null,直接return,從而退出loop()
            return null;
        }
        ........ 
        ........
        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        //-------------------分析點(diǎn)6---------------------------
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
}

??????從上面代碼可以看到,主要的點(diǎn)為:
??????分析點(diǎn)1:nativePollOnce(ptr, nextPollTimeoutMillis)是native方法,最終會(huì)調(diào)用到Linux的epoll_wait()進(jìn)行阻塞等待,nextPollTimeoutMillis是等待時(shí)間,當(dāng)消息隊(duì)列中沒有消息時(shí),就一直等待,直到通過nativeWeak()來喚醒;有消息但是沒有到消息的執(zhí)行時(shí)間時(shí),需要等待delay時(shí)間,然后執(zhí)行下面的邏輯;
??????分析點(diǎn)2:加入synchronized (this)來確保取message的正確性;
??????分析點(diǎn)3:消息屏障,即:msg.target == null,只能通過postSyncBarrier()來插入屏障消息,不能通過enqueueMessage()(會(huì)判斷msg.target==null,直接拋異常),當(dāng)消息隊(duì)列頭是屏障信息時(shí),會(huì)循環(huán)找到消息隊(duì)列中的異步消息(asynchronous),如果找到,執(zhí)行分析點(diǎn)4;如果未找到的話,會(huì)一直休眠,等待喚醒;

    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

??????從暴露的方法來看是hide及private的,應(yīng)用是調(diào)用不到的,除非通過反射調(diào)用,從hide的方法可以看到,調(diào)用該方法后,會(huì)將該屏障消息置于消息隊(duì)列的頭部,因?yàn)镾ystemClock.uptimeMillis()肯定小于當(dāng)前時(shí)間的,然后返回token;
??????前面分析到,在消息隊(duì)列中有消息屏障后,只會(huì)執(zhí)行隊(duì)列中的異步消息,那剩余的普通消息什么時(shí)候能夠被執(zhí)行呢?移除屏障消息:

    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            //----------------------判斷a----------------------------
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            //----------------------判斷b----------------------------
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

??????根據(jù)上面返回的token來移除屏障消息,然后判斷是否需要執(zhí)行nativeWake()來喚醒next(),來執(zhí)行隊(duì)列中的普通消息,判斷是否需要執(zhí)行nativeWake()條件如下:
??????判斷a:prev不為null,說明屏障消息不處于mMessages隊(duì)列頭,即還未執(zhí)行到,不必執(zhí)行喚醒;
??????判斷b:prev為null,說明屏障消息處于mMessages隊(duì)列頭,如果mMessages中有普通消息的話肯定未執(zhí)行,因此來判斷如果next是普通消息或mMessages為空時(shí),進(jìn)行nativeWeak();如果不為null,且next還是屏障信息,不需要nativeWake()。
??????分析點(diǎn)4:當(dāng)queue頭的message未到執(zhí)行時(shí)間時(shí),會(huì)計(jì)算出nextPollTimeoutMillis,最后通過nativePollOnce(ptr, nextPollTimeoutMillis)等待時(shí)間到來執(zhí)行message或新的nativeWeak();
??????分析點(diǎn)5:從queue頭取出message,然后queue頭指向下一個(gè)執(zhí)行的message,最后返回取出的message;
??????分析點(diǎn)6:當(dāng)隊(duì)列中下一個(gè)需要執(zhí)行的消息還未到時(shí)間(即需要等待nextPollTimeoutMillis),或消息隊(duì)列中沒有需要執(zhí)行的消息(nextPollTimeoutMillis=-1),此時(shí)可以執(zhí)行IdleHandler,即空閑的Handler,場景:1.延遲執(zhí)行,沒有固定時(shí)間;2.批量任務(wù),任務(wù)密集,且只關(guān)注最終結(jié)果;使用方式如下:

     Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            //do something
            return false;
        }
     });

4.Message

a.類型

??????根據(jù)前面的分析,消息有三種類型:普通消息、異步消息及消息屏障;
??????普通消息:常用的通過Message.obtain()來獲取的消息,target不能為null;
??????異步消息:和普通消息一樣,target不能為null,且通過setAsynchronous(true)設(shè)置,有了這個(gè)標(biāo)志位,消息機(jī)制會(huì)對(duì)它有些特別的處理;
??????消息屏障:也是一種消息,但是它的target為 null。只能通過MessageQueue中的postSyncBarrier方法發(fā)送一個(gè)消息屏障(該方法為私有,需要反射調(diào)用);
??????通過前面的分析可以看到,消息屏障和異步消息的作用很明顯,在設(shè)置消息屏障后,異步消息具有優(yōu)先處理的權(quán)利。比如:ViewRootImpl中UI繪制前會(huì)先發(fā)送消息屏障,再發(fā)送異步消息來繪制UI。

b.obtain()

??????在創(chuàng)建新的消息時(shí),不會(huì)去通過new Message來創(chuàng)建,而是通過obtain(),先看一下:

//創(chuàng)建Message,通過Message.obtain(xx,xx,...)最終會(huì)調(diào)用到該方法
//當(dāng)sPool不為空時(shí),會(huì)從sPool里面取出message,不會(huì)頻繁去new message()
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();
}

??????從上面代碼可以看到,主要的點(diǎn)為:
??????當(dāng)sPool不為null時(shí),說明已經(jīng)有執(zhí)行過的message,此時(shí)取出鏈表頭的msg1,然后把sPool指向sPool.next,假如sPool就存儲(chǔ)了一個(gè)message,則如果在msg1未執(zhí)行前,有新的message請(qǐng)求,需要執(zhí)行new message();

c.recycleUnchecked()

//釋放message,沒有delete,只是將內(nèi)容置空,然后把該消息放在sPool的head,供后續(xù)創(chuàng)建新的message使用
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++;
        }
    }
}

??????在message處理完后,會(huì)調(diào)用recycleUnchecked()來將Message內(nèi)容置空,然后判斷當(dāng)sPoolSize小于50時(shí),將剛執(zhí)行完的msg的next指向sPool,sPool指向該msg,即把該msg作為表頭,然后size++;當(dāng)sPoolSize大于50時(shí),就不會(huì)再插入了;
??????通過以上分析,執(zhí)行完的message以鏈表形式存儲(chǔ),獲取message時(shí),會(huì)取出sPool表頭的msg,然后sPool指向next、size--;該種創(chuàng)建及獲取消息方式的好處:內(nèi)存復(fù)用,防止內(nèi)存抖動(dòng)(不斷的創(chuàng)建,釋放),用到了享元設(shè)計(jì)模式

四.代碼實(shí)現(xiàn)

a.創(chuàng)建一個(gè)Handler

//創(chuàng)建Handler
private final UIHandler mHandler = new UIHandler(this);
private static class UIHandler extends Handler {

    private static final int MSG_UPDATE_BG = 1;
    //防止內(nèi)存泄露,使用弱引用,否則Handler會(huì)持有Activity的強(qiáng)引用,影響內(nèi)存回收
    private final WeakReference<DisplayActivity> mActivity;

    private UIHandler(DisplayActivity activity) {
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        DisplayActivity activity = mActivity.get();
        if (activity == null) {
            FLog.e(TAG,"activity is null, handleMessage return");
            super.handleMessage(msg);
            return;
        }
        switch (msg.what) {
            case MSG_UPDATE_BG:
                activity.updateUI(msg.arg1, msg.arg2, (boolean) msg.obj);
                break;
            default:
                super.handleMessage(msg);
                break;
        }
    }
}

b.使用Handler發(fā)送消息

//簡單的消息只傳遞what
mHandler.sendEmptyMessageDelayed(UIHandler.MSG_UPDATE_BG, 500);
//通過obtainMessage,可以傳遞what,arg1,arg2及obj參數(shù)
mHandler.sendMessage(mHandler.obtainMessage(UIHandler.MSG_UPDATE_BG1,
                display, statusCode, false));

c.處理消息

   @Override
    public void handleMessage(Message msg) {
        DisplayActivity activity = mActivity.get();
        if (activity == null) {
            FLog.e(TAG,"activity is null, handleMessage return");
            super.handleMessage(msg);
            return;
        }
        switch (msg.what) {
            case MSG_UPDATE_BG:
                activity.updateUI(msg.arg1, msg.arg2, (boolean) msg.obj);
                break;
            default:
                super.handleMessage(msg);
                break;
        }
    }

??????上面已經(jīng)一步步的解釋了一個(gè)消息從發(fā)送到處理的整個(gè)流程,用流程圖概況Handler的工作流程如下:


1.png

五.ThreadLocal

??????在主線程里面創(chuàng)建多個(gè)Handler的時(shí)候,為什么都能更新UI?換句話說,為什么都能共享主線程的Looper?那么就用到了ThreadLocal這個(gè)東西了,一起看一下。
??????對(duì)ThreadLocal的誤解:
??????1.ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路
??????2.ThreadLocal的目的是為了解決多線程訪問資源時(shí)的共享問題
??????官方注釋:

/**
 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).
*/

??????從注釋可以看到:ThreadLocal類用來提供線程內(nèi)部的局部變量。這種變量在多線程環(huán)境下訪問(通過get或set方法訪問)時(shí)能保證各個(gè)線程里的變量相對(duì)獨(dú)立于其他線程內(nèi)的變量。ThreadLocal實(shí)例通常來說都是private static類型的,用于關(guān)聯(lián)線程和線程的上下文。
??????概括如下:
??????1.ThreadLocal的作用是提供線程內(nèi)的局部變量,這種變量在線程的生命周期內(nèi)起作用,減少同一個(gè)線程內(nèi)多個(gè)函數(shù)或者組件之間一些公共變量的傳遞的復(fù)雜度。
??????2.ThreadLocal的設(shè)計(jì)初衷就是為了提供線程內(nèi)部的局部變量,方便在本線程內(nèi)隨時(shí)隨地的讀取,并且與其他線程隔離。
??????3.ThreadLocal實(shí)現(xiàn)了一個(gè)線程相關(guān)的存儲(chǔ),即每個(gè)線程都有自己獨(dú)立的變量。所有的線程都共享這一個(gè)ThreadLocal對(duì)象,并且當(dāng)一個(gè)線程的值發(fā)生改變之后,不會(huì)影響其他的線程的值。
??????從Looper.java代碼分析一下:

// sThreadLocal.get() will return null unless you've called prepare().
// 用ThreadLocal存儲(chǔ)Looper,除非調(diào)用prepare(),否則的話sThreadLocal.get()會(huì)返回null
// ThreadLocal自身有一個(gè)ThreadLocalMap,來管理所有線程對(duì)應(yīng)的變量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

//在prepare里面創(chuàng)建Looper
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //創(chuàng)建Looper,執(zhí)行set()
    sThreadLocal.set(new Looper(quitAllowed));
}

//獲取當(dāng)前的Looper
public static @Nullable Looper myLooper() {
    //通過sThreadLocal.get()獲取
    return sThreadLocal.get();
}

??????從ThreadLocal.java代碼分析一下:

public T get() {
    //獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    //獲取當(dāng)前線程對(duì)應(yīng)的map
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //從線程對(duì)應(yīng)的map里面取變量值
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
    //threadLocals是線程的變量
    return t.threadLocals;
}

??????前面分析到,Looper.myLooper()內(nèi)部是通過sThreadLocal.get()來返回Looper的,在get()方法內(nèi),會(huì)先獲取到當(dāng)前線程,然后獲取到線程對(duì)應(yīng)的map,最后從map中取出對(duì)應(yīng)的value。

public void set(T value) {
    //獲取當(dāng)前線程
    Thread t = Thread.currentThread();
    //獲取當(dāng)前線程對(duì)應(yīng)的map
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //向線程對(duì)應(yīng)的map里面給變量賦值
        map.set(this, value);
    else
        //首次賦值時(shí)需要?jiǎng)?chuàng)建map
        createMap(t, value);
}

void createMap(Thread t, T firstValue) {
    //對(duì)當(dāng)前線程的threadLocals進(jìn)行賦值
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

??????前面分析到,Looper.prepare()內(nèi)部是通過sThreadLocal.set(new Looper())來存儲(chǔ)Looper的,在set()方法內(nèi),會(huì)先獲取到當(dāng)前線程,然后獲取到線程對(duì)應(yīng)的map,如果為null,就創(chuàng)建map,然后存入只;不為null,就直接更新map的值即可。
??????看一下getMap(t)取出的threadLocals是從何而來?如何確保Looper的唯一性?

//Thread.java
/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

??????ThreadLocal通過獲取當(dāng)前線程中的values屬性,從而實(shí)現(xiàn)了每個(gè)單獨(dú)線程的信息綁定。在Android的消息機(jī)制中,Looper便是采用ThreadLocal作為存儲(chǔ)結(jié)構(gòu),所以looper對(duì)象的存儲(chǔ)只會(huì)在當(dāng)前線程中,子線程若是使用消息機(jī)制的話,必須調(diào)用Looper.prepare()方法來在線程中新建一個(gè)Looper的對(duì)象。
線程中的Looper唯一性:
??????1.Looper創(chuàng)建是在prepare()里面觸發(fā)的,當(dāng)get()不為null時(shí),會(huì)拋異常,確保不會(huì)重復(fù)創(chuàng)建;當(dāng)get()為null時(shí),會(huì)先通過setInitialValue為對(duì)應(yīng)線程的ThreadLocalMap變量進(jìn)行賦值,后面通過set()時(shí),會(huì)調(diào)用ThreadLocalMap的set():key是sThreadLocal,static final修飾,確保key和value唯一;
??????2.一個(gè)線程對(duì)應(yīng)唯一的ThreadLocalMap變量,當(dāng)去get()時(shí),會(huì)根據(jù)當(dāng)前線程去獲取該線程的ThreadLocalMap變量,通過ThreadLocalMap去獲取對(duì)應(yīng)的Looper,確保一個(gè)線程對(duì)應(yīng)一個(gè)Looper。

六.總結(jié)

a.Handler機(jī)制中最重要的四個(gè)對(duì)象:

??????Handler:負(fù)責(zé)發(fā)送消息及處理消息
??????Looper:復(fù)制不斷的從消息隊(duì)列中取出消息,并且將消息給發(fā)送本條消息的Handler
??????MessageQueue:負(fù)責(zé)存儲(chǔ)消息
??????Message:消息本身,負(fù)責(zé)攜帶數(shù)據(jù)

b.Looper

??????每一個(gè)線程中都對(duì)應(yīng)一個(gè)Looper[sThreadLocal存儲(chǔ)],通過Looper.prepare()為當(dāng)前線程創(chuàng)建Looper,通過Looper.myLooper()獲得當(dāng)前線程的Looper對(duì)象,每一個(gè)Looper都對(duì)應(yīng)一個(gè)MessageQueue,一個(gè)Handler對(duì)應(yīng)唯一Looper,一個(gè)Looper可以對(duì)應(yīng)多個(gè)Handler;

c.引用關(guān)系

??????Handler持有Activity的引用,Message持有Handler的引用,MessageQueue持有Message的引用,Looper持有MessageQueue的引用,ActivityThread的main()中創(chuàng)建了sMainLooper;

d.內(nèi)存泄露問題

??????注意Handler使用導(dǎo)致的內(nèi)存泄漏問題,在Java中,非靜態(tài)的內(nèi)部類和匿名內(nèi)部類都會(huì)隱式地持有其外部類的引用。靜態(tài)的內(nèi)部類不會(huì)持有外部類的引用,所以不會(huì)導(dǎo)致外部類實(shí)例的內(nèi)存泄露。當(dāng)需要在靜態(tài)內(nèi)部類中調(diào)用外部的Activity時(shí),可以使用弱引用來處理。

七.常見問題

a.一個(gè)線程有幾個(gè)Handler?

??????可以有N個(gè)Handler;

b.一個(gè)線程有幾個(gè)Looper,如何保證?

??????一個(gè)線程有1個(gè)Looper;
??????Looper創(chuàng)建是在prepare()里面觸發(fā)的,當(dāng)sThreadLocal.get()不為null時(shí),會(huì)拋異常,確保不會(huì)重復(fù)創(chuàng)建,另外在存儲(chǔ)Looper時(shí),用的是對(duì)應(yīng)線程的ThreadLocalMap,然后通過ThreadLocalMap存儲(chǔ)時(shí)key用的是sThreadLocal,保證key和value唯一;
??????一個(gè)線程對(duì)應(yīng)唯一的ThreadLocalMap變量,當(dāng)在sThreadLocal去get()時(shí),會(huì)根據(jù)當(dāng)前線程去獲取該線程的ThreadLocalMap,通過ThreadLocalMap去獲取對(duì)應(yīng)的Looper,確保一個(gè)線程對(duì)應(yīng)一個(gè)Looper。

c.Handler內(nèi)存泄露原因?為什么其他內(nèi)部類沒有該問題?

??????發(fā)送的延遲消息(EmptyMessageDelayed)后、消息處理被前,該消息會(huì)一直保存在主線程的消息隊(duì)列里持續(xù)時(shí)間,在持續(xù)時(shí)間里,該消息內(nèi)部持有對(duì)handler的引用,由于handler屬于非靜態(tài)內(nèi)部類,所以又持有對(duì)其外部類(即MainActivity實(shí)例)的潛在引用,引用關(guān)系如下:


1.png

??????這條引用關(guān)系會(huì)一直保持直到消息得到處理,從而阻止了MainActivity被垃圾回收器(GC)回收,同時(shí)造成應(yīng)用程序的內(nèi)存泄漏,如下:


2.png

??????其他內(nèi)部類中沒有持續(xù)持有該內(nèi)部類應(yīng)用的東西,則內(nèi)部類就不會(huì)存在一直持有外部類引用的場景,所以不會(huì)造成內(nèi)存泄露。

d.為何主線程可以new Handler?如果在子線程new Handler需要做什么工作?

??????因?yàn)橹骶€程一啟動(dòng)的時(shí)候,在main()函數(shù)中,由系統(tǒng)已經(jīng)幫我們完成了,我們主線程中的所有代碼,全都運(yùn)行在這兩個(gè)函數(shù)(prepare() 和 loop())之間。
??????所有的線程在使用Handler時(shí)都必須要prepare()和loop(),如果子線程中想要進(jìn)行Handler操作,就必須在子線程中執(zhí)行prepare() 和 loop();
??????Looper.prepare(); Handler handler = new Handler();Looper.loop();
??????子線程如果沒有消息處理時(shí),則需要執(zhí)行Looper.quitSafely();主線程不允許quit(),執(zhí)行quit方法時(shí)會(huì)拋異常。

e.子線程維護(hù)的Looper,消息隊(duì)列無消息時(shí)的處理方案是什么?有什么用?

??????當(dāng)消息隊(duì)列無消息時(shí),loop()會(huì)block在Message msg = queue.next();
??????調(diào)用Looper.quitSafely()方法,在Handler機(jī)制里面有一個(gè)Looper,在Looper機(jī)制里面有一個(gè)函數(shù),叫做quitSafely()和quit()函數(shù)。
??????這兩個(gè)函數(shù)是調(diào)用的MessageQueue的quit()方法,執(zhí)行nativeWake(mPtr),喚醒MessageQueue的next()里面的nativePollOnce(),最終返回message為null,loop()方法在判斷message為null后會(huì)return,退出loop()。
??????1.remove消息,把消息隊(duì)列中的全部消息給干掉,也就釋放了內(nèi)存;達(dá)成了第一個(gè)作用:釋放內(nèi)存
??????2.Looper結(jié)束(跳出死循環(huán)),則達(dá)成了第二個(gè)作用:釋放線程

f.既然可以存在多個(gè)Handler往MessageQueue中添加數(shù)據(jù)(發(fā)消息時(shí)各個(gè)Handler可能處于不同的線程),那么它內(nèi)部是如何確保線程安全的?

??????synchronized()

g.使用Message時(shí)應(yīng)該如何創(chuàng)建它?

??????通過Message.obtain()來創(chuàng)建Message ;
??????Message會(huì)維護(hù)一個(gè)大小為50的sPool,每次釋放message時(shí),只是把內(nèi)容置空;有新消息創(chuàng)建時(shí),從sPool里面取出head位置的message,然后初始化即可;
??????內(nèi)存復(fù)用,防止內(nèi)存抖動(dòng)(不斷的創(chuàng)建,釋放)
??????享元設(shè)計(jì)模式

h.Looper死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死?

??????卡死:是anr,輸入事件在指定的時(shí)間內(nèi)沒有響應(yīng)。
??????loop():是睡眠,沒有消息處理時(shí),會(huì)block在Message msg = queue.next(),當(dāng)有消息觸發(fā)時(shí),會(huì)喚醒執(zhí)行 ;
??????Looper.loop不斷的接收處理事件,每一個(gè)點(diǎn)擊觸摸或者Activity每一個(gè)生命周期都是在Looper.loop()的控制之下進(jìn)行的。

i.消息屏障和異步消息

??????上面分析到,消息有三種類型普通消息、同步屏障消息和異步消息;因?yàn)橄⑻幚碛休p重緩急,就誕生了同步屏障消息和異步消息。它們倆是配套使用的,當(dāng)消息隊(duì)列中同時(shí)存在這三種消息時(shí),如果碰到了同步屏障消息,那么會(huì)優(yōu)先執(zhí)行異步消息;比如:ViewRootImpl中的scheduleTraverals()中會(huì)先發(fā)送屏障消息,然后通過Choreographer發(fā)送異步消息來確保UI繪制優(yōu)先執(zhí)行。

j.IdleHandler

??????IdleHandler是一個(gè)接口,里面有queueIdle()方法,從字面意思來看是空閑的Handler,通過MessageQueue中的addIdleHandler()執(zhí)行,在前面的分析MessageQueue的next()方法中,如果消息隊(duì)列中沒到消息執(zhí)行時(shí)間或沒有消息時(shí),會(huì)循環(huán)處理IdleHandler;適合場景:1.延遲執(zhí)行,沒有確切的時(shí)間;2.批量任務(wù),任務(wù)密集,且只關(guān)注最終結(jié)果;

??????以上就是對(duì)Handler消息機(jī)制的詳細(xì)介紹及常見的問題,后續(xù)有新的收獲會(huì)繼續(xù)更新!

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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