Android 面試準(zhǔn)備進(jìn)行曲 (Handler源碼/面試題)v1.3

本博客為本人學(xué)習(xí) Handler 中的筆記,可能文筆方面欠妥搂捧,該文中本人會(huì)描述主要源碼和部分經(jīng)典面試題答案的源碼解讀认然。閱讀時(shí)間:20分鐘+ 本文源碼版本 android SDK 28

update time : 2019年12月30日19:01:22
v1.2 : 添加面試題(message復(fù)用 和 Handler.post 的流程)
v1.3 : 修改個(gè)人本身對(duì) MessageQueue 理解錯(cuò)誤,重點(diǎn)修改 Message 單鏈表對(duì)應(yīng)的知識(shí)點(diǎn)

前言

關(guān)于Google 建議在主線程中更新UI (其實(shí)子線程也可以更新UI,但是不推薦)多線程同步更新UI 科侈,容易使UI進(jìn)入不可預(yù)測(cè)的狀態(tài)四苇。
將工作線程中需更新UI的操作信息 傳遞到 UI主線程装处,從而實(shí)現(xiàn) 工作線程對(duì)UI的更新繪制等處理闭翩,最終實(shí)現(xiàn)異步消息的處理挣郭。(保證多線程安全 數(shù)據(jù)更新的順序性)

Handler 流程

轉(zhuǎn)載圖片原文

在這里插入圖片描述

上圖已經(jīng)比較清晰地講述了整個(gè)handler 的過(guò)程其中牽扯到比較重要的 類有:

  • 處理器 類(Handler)
  • 消息隊(duì)列 類(MessageQueue)
  • 循環(huán)器 類(Looper)

三個(gè)對(duì)象直接的關(guān)系

Handler -> Handler.sendMessage() / post() -> MessageQueue -> MessageQueue.next() -> Looper -> Looper.prepare() -> Looper.loop() -> handlerMessage() 回到Handler中。
關(guān)于MessageQueue先說(shuō)明一點(diǎn)疗韵,該隊(duì)列的實(shí)現(xiàn)既非Collection的子類兑障,亦非Map的子類,而是Message本身蕉汪。因?yàn)镸essage本身就是鏈表節(jié)點(diǎn)流译。

流程源碼

這里主要講述三個(gè)對(duì)象整個(gè)流程的源碼分析,順帶 說(shuō)一些面試題的點(diǎn)

Handler 初始化

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());
            }
        }
        // 內(nèi)部通過(guò) ThreadLocal<Looper>.get() 獲取的 唯一的loop對(duì)象
        mLooper = Looper.myLooper();    
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // 獲取該Looper對(duì)象中保存的消息隊(duì)列對(duì)象(MessageQueue)
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

上述代碼為Handler 中的構(gòu)造方法部分,mLooper = Looper.myLooper() 通過(guò)ThreadLocal<Looper>.get() 獲取到 Loop對(duì)象中 的Loop存儲(chǔ)對(duì)象者疤,這里也就保證了 一個(gè)線程可以有多個(gè)Handler 福澡,一個(gè)Handler 只能綁定一個(gè) Looper。有g(shù)et()方法了我們就找一下 在那個(gè)地方set進(jìn)去的宛渐。接下來(lái)我們進(jìn)入Looper 源碼部分。

Looper 初始化

// quitAllowed 參數(shù)為 MessageQueue 是否可以quit
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));
    }

    
    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 Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

通過(guò)上述代碼 在 prepare() 中為sThreadLocal 傳入新的Looper 對(duì)象眯搭,Looper()中創(chuàng)建了 messageQueue 窥翩,獲取當(dāng)前線程的 對(duì)象。
所以 一個(gè)線程可以有多個(gè)Handler 鳞仙,一個(gè)Handler 只能綁定一個(gè) Looper 寇蚊,一個(gè)Looper 中有一個(gè)MessageQueus (好他瞄的繞 但是面試官真的這樣問(wèn)過(guò)我),通過(guò)反推 我們可以 給面試官講述 Handler Looper MessageQueus 之間的 創(chuàng)建聲明關(guān)系棍好。

prepareMainLooper()對(duì)應(yīng)著 創(chuàng)建主線程時(shí)仗岸,會(huì)自動(dòng)調(diào)用ActivityThread的1個(gè)靜態(tài)的main();而main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對(duì)象借笙,同時(shí)也會(huì)生成其對(duì)應(yīng)的MessageQueue對(duì)象扒怖,這也就是我們?yōu)楹尾挥迷?OnCreate 的時(shí)候 設(shè)置Looper.prepare()

Handler 發(fā)送消息

sendMessage() / postMessage() 發(fā)送消息 业稼,post() 則是 實(shí)現(xiàn)一個(gè) Runable 不需要外部創(chuàng)建消息對(duì)象盗痒。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
// 跳轉(zhuǎn)到sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
 //  跳轉(zhuǎn)到 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);
    }

核心代碼 就可以看到 queue.enqueueMessage() 方法,我們來(lái)看一下源碼

// enqueueMessage核心辦法

Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
        // 如果Message隊(duì)列為空的 則將信息添加到頭部 
        msg.next = p;
        mMessages = msg;
    } else {
        ...
        Message prev;
        for (;;) {
            // 通過(guò) prev = p低散、 p.next 遞歸鏈表 比對(duì)when
            prev = p;
            p = p.next;
            if (p == null || when < p.when) {
                 break;
            }
            ...
        }
    msg.next = p; // invariant: p == prev.next
    prev.next = msg;
 }

Message 通過(guò) next 來(lái)將Message 本身當(dāng)做節(jié)點(diǎn) 拼接為單鏈表俯邓,when 代表的就是 Message 觸發(fā)的時(shí)間骡楼,所以循環(huán) 比對(duì)時(shí)間 如果觸發(fā) when < p.when,則退出循環(huán) 進(jìn)行插入 p 的操作稽鞭。

Looper.loop -> MessageQueue.next

消息循環(huán)鸟整,即從消息隊(duì)列中獲取消息、分發(fā)消息到Handler 朦蕴。下為部分核心源碼

public static void loop() {

        // 1. 獲取當(dāng)前Looper的消息隊(duì)列
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            // myLooper()作用:返回sThreadLocal存儲(chǔ)的Looper實(shí)例
            final MessageQueue queue = me.mQueue;
            for (;;) {
            
                // 從消息隊(duì)列中取出消息
                Message msg = queue.next(); 
                if (msg == null) {
                    return;
                }
            // next():取出消息隊(duì)列里的消息
            // 若取出的消息為空篮条,則線程阻塞
            // 派發(fā)消息到對(duì)應(yīng)的Handler
            msg.target.dispatchMessage(msg);
            // 把消息Message派發(fā)給消息對(duì)象msg的target屬性
            // target屬性實(shí)際是1個(gè)handler對(duì)象
            msg.recycleUnchecked();
        }
}

通過(guò) Looper 獲取到 MessageQueue ,然后從 queue 中 取出消息梦重,然后回調(diào) msg中的 target 走dispatchMessage方法 回調(diào) 到我們的主線程的Handler 的 handlermessage () 方法中兑燥,然后 在調(diào)用recycleUnchecked方法將 msg 標(biāo)識(shí)和 next 等標(biāo)記設(shè)置為初始值。

下邊我們進(jìn)入 MessageQueue.next() / dispatchMessage(msg) 看看源碼

Message next() {
        // 該參數(shù)用于確定消息隊(duì)列中是否還有消息
        // 從而決定消息隊(duì)列應(yīng)處于出隊(duì)消息狀態(tài) / 等待狀態(tài)
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

        // nativePollOnce方法在native層琴拧,若是nextPollTimeoutMillis為-1降瞳,此時(shí)消息隊(duì)列處于等待狀態(tài) 
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
     
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;

            // 出隊(duì)消息,從消息隊(duì)列中取出消息:按創(chuàng)建Message對(duì)象的時(shí)間順序
            if (msg != null) {
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 取出消息
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 若 消息隊(duì)列中已無(wú)消息蚓胸,則將nextPollTimeoutMillis參數(shù)設(shè)為-1
                // 下次循環(huán)時(shí)挣饥,消息隊(duì)列則處于等待狀態(tài)
                nextPollTimeoutMillis = -1;
            }
            ......
        }
           .....
       }
}

上述代碼主要功能都有注釋 表明了,通過(guò) Looper.loop 到 msgQueue.next 循環(huán) 隊(duì)列沛膳,獲取有用的消息進(jìn)行出列操作扔枫,沒(méi)有消息就更新標(biāo)識(shí),阻塞循環(huán)锹安。
上文的 msg.target.dispatchMessage(msg); 其實(shí)target 就是Handler 對(duì)象 我們通過(guò)跟蹤找到 dispatchMessage 源碼

Handler 消息處理源碼

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


    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }
    
    // 我們必須實(shí)現(xiàn)的 子類方法
    public void handleMessage(Message msg) {
    }

在進(jìn)行消息分發(fā)時(shí)dispatchMessage(msg)短荐,會(huì)進(jìn)行1次發(fā)送方式的判斷:
若msg.callback屬性不為空,則代表使用了post(Runnable r)發(fā)送消息叹哭,則直接回調(diào)Runnable對(duì)象里復(fù)寫(xiě)的run()
若msg.callback屬性為空忍宋,則代表使用了sendMessage(Message msg)發(fā)送消息,則回調(diào)復(fù)寫(xiě)的handleMessage(msg)

面試題

Message 創(chuàng)建回收 鏈表關(guān)系

這里分享一個(gè) 有圖 有源碼 自我感覺(jué)不錯(cuò)的博客
Message中obtain()與recycle()的來(lái)龍去脈

MessageQueue 創(chuàng)建的時(shí)間

上文Looper 在創(chuàng)建的時(shí)候有源碼 messageQueue 是和Looper 一塊創(chuàng)建的

ThreadLocal在Handler機(jī)制中的作用

sThreadLocal是一個(gè)ThreadLocal對(duì)象风罩,可以在一個(gè)線程中存儲(chǔ)變量糠排。上文 Looper.prepare() 源碼出可以看到 從中g(shù)et到一個(gè) Looper 對(duì)象,sThreadLocal存儲(chǔ)的就是Looper 對(duì)象超升。也就說(shuō)明了 一個(gè)handler 對(duì)應(yīng)一個(gè)Looper 入宦。

Looper,Handler,MessageQueue的引用關(guān)系?

這里直接 引用上文 寫(xiě)的:“ Handler -> Handler.sendMessage() / post() -> MessageQueue -> MessageQueue.next() -> Looper -> Looper.prepare() -> Looper.loop() -> handlerMessage() 回到Handler中“ 。

Handler post 和 sendMessage 區(qū)別

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    

查看post源碼 里邊其實(shí)還是走的SendMessage 室琢,不過(guò) post方法對(duì) msg的callback和target都有賦值乾闰,上文提到的 Handler.dispatchMessage 方法中有提到 對(duì)這兩種方法有一個(gè)區(qū)分 反饋處理。

Handler導(dǎo)致內(nèi)存泄露問(wèn)題?

這個(gè)問(wèn)題在 三年工作經(jīng)驗(yàn)以后穩(wěn)得比較少 盈滴,主要考察 生命周期 和gc 的問(wèn)題汹忠。因?yàn)?匿名內(nèi)部類 Handler 中包含 外部對(duì)象 也就是Activity 對(duì)象,如果界面銷毀但是 后邊的Queue還在 消息沒(méi)有處理,就有可能造成 內(nèi)存泄漏宽菜。

簡(jiǎn)單的處理方法:

static class MyHandler extends Handler {
    // 弱引用 Avtivity 
    WeakReference mActivityReference;
    // 如果界面銷毀 handler 交給 gc自己清理
    MyHandler(Activity activity) {
        mActivityReference= new WeakReference(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
            mImageView.setImageBitmap(mBitmap);
        }
    }
}

有了解過(guò) Handler的同步屏障機(jī)制么谣膳?

這一點(diǎn)說(shuō)實(shí)話有點(diǎn)冷門(mén) 但是也可以看出你對(duì)源碼的解讀是否深刻,(我也是從朋友 強(qiáng)哥哪里聽(tīng)來(lái)的 360大佬牛逼)
在說(shuō) 同步屏障的前提铅乡,我們需要先說(shuō) Message 可以添加 同步消息和 消息

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

上述代碼的 async 布爾值 便是同步和異步的標(biāo)識(shí)

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

在加入 Message 的時(shí)候 調(diào)用msg.setAsynchronous(true)

同步屏障就是為了 區(qū)分 MessageQueue 中的message 的優(yōu)先級(jí)的继谚。通過(guò)打開(kāi) 同步屏障 可以在 looper 循環(huán)的時(shí)候使 異步消息優(yōu)先處理并返回。我們通過(guò)源碼看看:

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

注意這里 Message 的tager 其實(shí)為null ,我們重新貼一下 Looper.loop () 看下 源碼怎么處理的

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            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) {
                    // 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) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        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 {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

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

                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            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;
        }
    }

從上面的代碼中阵幸,我們知道花履,當(dāng)MessageQueue取到一個(gè)target為null的Message是,會(huì)先執(zhí)行異步消息挚赊,已達(dá)到 后添加進(jìn)去的消息诡壁,先處理通過(guò) 所謂的同步屏障 達(dá)到修改鏈表順序處理的機(jī)制。

主線程Looper.loop 為何不會(huì)卡死

由于這個(gè)問(wèn)題 其實(shí)我自己當(dāng)時(shí)也不是很明白 給出 參考文章
這個(gè)問(wèn)題 涵蓋面就有點(diǎn)多了荠割,不光要說(shuō) Handler 還有說(shuō) linux epoll機(jī)制.
為了可以讓App一直運(yùn)行 妹卿,我們是絕不希望會(huì)被運(yùn)行一段時(shí)間,自己就退出蔑鹦,那么如何保證能一直存活呢夺克?簡(jiǎn)單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的,死循環(huán)便能保證不會(huì)被退出嚎朽,例如铺纽,binder線程也是采用死循環(huán)的方法,通過(guò)循環(huán)方式不同與Binder驅(qū)動(dòng)進(jìn)行讀寫(xiě)操作哟忍,當(dāng)然并非簡(jiǎn)單地死循環(huán)狡门,無(wú)消息時(shí)會(huì)休眠。
這里就涉及到Linux pipe/epoll機(jī)制锅很,簡(jiǎn)單說(shuō)就是在主線程的MessageQueue沒(méi)有消息時(shí)其馏,便阻塞在loop的queue.next()中的nativePollOnce()方法里,詳情見(jiàn)Android消息機(jī)制1-Handler(Java層)粗蔚,此時(shí)主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài)尝偎,直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生饶火,通過(guò)往pipe管道寫(xiě)端寫(xiě)入數(shù)據(jù)來(lái)喚醒主線程工作鹏控。這里采用的epoll機(jī)制,是一種IO多路復(fù)用機(jī)制肤寝,可以同時(shí)監(jiān)控多個(gè)描述符当辐,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮骼鹂矗举|(zhì)同步I/O缘揪,即讀寫(xiě)是阻塞的。 所以說(shuō),主線程大多數(shù)時(shí)候都是處于休眠狀態(tài)找筝,并不會(huì)消耗大量CPU資源蹈垢。
關(guān)于更加詳細(xì)的 和本片文章Handler 沾邊比較少的 ,則可以異步鏈接地址觀看 Git yuan大佬的詳細(xì)解讀袖裕。

Message 復(fù)用機(jī)制

在Loop.looper 中我們 就可以初探原因曹抬。

// Loop 類

for (;;) {
            Message msg = queue.next(); // might block
            ...
            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            ...

            try {
                // 消息分發(fā) target 其實(shí)指的 Handler (這里也是 Handler sendMsg 和 post 方法的區(qū)別所在)
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...

            final long newIdent = Binder.clearCallingIdentity();
            // 回收方法 我們重點(diǎn)看一下這個(gè)方法
            msg.recycleUnchecked();
        }


// Message 類

void recycleUnchecked() {
        // 改變標(biāo)記 加入消息池中  并重置所有狀態(tài)
        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) {
                // 頭節(jié)點(diǎn)設(shè)置給Next 將當(dāng)前對(duì)象最為最新的頭節(jié)點(diǎn)sPool 
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

我們還要看下 關(guān)于 Message .obtain() 中如何 處理的


// 部分需要標(biāo)注的對(duì)象
 public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;

// 消息回首復(fù)用主要方法 
public static Message obtain() {
        synchronized (sPoolSync) {
            //判斷頭節(jié)點(diǎn)是否null 
            if (sPool != null) {  
                // 取出頭結(jié)點(diǎn) 并將下一個(gè)消息設(shè)置為頭結(jié)點(diǎn)
                Message m = sPool;  
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        // 如果消息鏈表為null 則返回新msg
        return new Message();
 }

Message通過(guò)靜態(tài)單鏈表來(lái)全局完成消息的復(fù)用,而在每次回收的過(guò)程中消息數(shù)據(jù)重置防止Message持有其他對(duì)象而造成內(nèi)存泄漏操作急鳄,所有在日常開(kāi)發(fā)開(kāi)發(fā)中盡量使用Mesaage.obtain 來(lái)獲取Message谤民。

MessageQueue.removeMessages中的操作

這一部分也是 朋友魚(yú)總 問(wèn)我的,答不上來(lái) 進(jìn)行了一次百度疾宏,并附上參考文章地址 :MessageQueue.removeMessages中的操作

Handler.post

Handler.sendmsg 和 Handler.post 兩種方式 不同的是 post方法只需要完成 runable 方法實(shí)現(xiàn)就好张足,他底層就做 了什么操作呢?

// handler.class
public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }


private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        // 對(duì)msg的 callback 進(jìn)行賦值
        m.callback = r;
        return m;
    }



//  在Loop.loop 中 對(duì)消息進(jìn)行的分發(fā)代碼中 對(duì)msg的callback進(jìn)行分類處理坎藐。

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {

            // 回調(diào) post 方法中的 runable 方法
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }

            // 回到到 handler 的 handleMessage 方法中
            handleMessage(msg);
        }
    }

部分講解 都在代碼的注釋中了为牍。大體handler 本人學(xué)習(xí)和部分面試題總結(jié) 大概就這些了。后續(xù)還會(huì)持續(xù)維護(hù)更新顺饮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吵聪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子兼雄,更是在濱河造成了極大的恐慌吟逝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赦肋,死亡現(xiàn)場(chǎng)離奇詭異块攒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)佃乘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)囱井,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人趣避,你說(shuō)我怎么就攤上這事庞呕。” “怎么了程帕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵住练,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我愁拭,道長(zhǎng)讲逛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任岭埠,我火速辦了婚禮盏混,結(jié)果婚禮上蔚鸥,老公的妹妹穿的比我還像新娘。我一直安慰自己许赃,他們只是感情好止喷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著混聊,像睡著了一般启盛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上技羔,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天僵闯,我揣著相機(jī)與錄音,去河邊找鬼藤滥。 笑死鳖粟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拙绊。 我是一名探鬼主播向图,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼标沪!你這毒婦竟也來(lái)了榄攀?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤金句,失蹤者是張志新(化名)和其女友劉穎檩赢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體违寞,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贞瞒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了趁曼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片军浆。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖挡闰,靈堂內(nèi)的尸體忽然破棺而出乒融,到底是詐尸還是另有隱情,我是刑警寧澤摄悯,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布赞季,位于F島的核電站,受9級(jí)特大地震影響射众,放射性物質(zhì)發(fā)生泄漏碟摆。R本人自食惡果不足惜晃财,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一叨橱、第九天 我趴在偏房一處隱蔽的房頂上張望典蜕。 院中可真熱鬧,春花似錦罗洗、人聲如沸愉舔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)轩缤。三九已至,卻和暖如春贩绕,著一層夾襖步出監(jiān)牢的瞬間火的,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工淑倾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留馏鹤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓娇哆,卻偏偏與公主長(zhǎng)得像湃累,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碍讨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容