深入分析Android Handler中的知識點

Handler發(fā)送Message流程

  • 從主線程new Handler()開始:
<Handler.java>
    public Handler() {
        this(null, false);
    }

    public Handler(@Nullable 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());
            }
        }
        // 獲取當(dāng)前線程的looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // 獲取當(dāng)前線程的messageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;// 回調(diào)
        mAsynchronous = async;// 設(shè)置異步標(biāo)記,異步消息會優(yōu)先處理
    }

<Lopper.java>
    // 通過ThreadLocal保證每個線程只有一個looper
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
  • 主線程的Looper是在應(yīng)用程序啟動時ActivityThread為我們創(chuàng)默認(rèn)創(chuàng)建了肖粮,所以上面new Handler()中可以直接獲取到主線程的Looper
<ActivityThread.java>
    public static void main(String[] args) {
        ...
        // 構(gòu)造主線程Looper對象
        Looper.prepareMainLooper();

        ...
        ...
        // 應(yīng)用運行期間所有代碼都運行在此位置,如果主線程Looper退出循環(huán),則應(yīng)用運行結(jié)束

        // 開啟消息循環(huán)
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
  • Looper初始化和準(zhǔn)備過程
<Looper.java>
    public static void prepareMainLooper() {
        // 創(chuàng)建實例
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 變量賦值
            sMainLooper = myLooper();
        }
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {// 一個線程只能有一個Looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // new了一個Looper對象
        sThreadLocal.set(new Looper(quitAllowed));
    }

    // 開始死循環(huán)獲取消息
    public static void loop() {
        final Looper me = myLooper();// 獲取當(dāng)前線程looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;// 獲取到當(dāng)前消息隊列

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

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        // 開啟死循環(huán)輪詢消息
        for (;;) {
            // 從消息隊列中獲取下一個未處理的消息,重點愁溜,后面分析
            Message msg = queue.next(); // might block
            // 消息隊列為空,沒有消息了外厂,此時說明退出應(yīng)用了
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ...
            try {
                // 消息分發(fā)回調(diào)給handler處理
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } 
            ...
            // 回收消息加入復(fù)用池
            msg.recycleUnchecked();
        }
    }
  • new Handler()以及主線程Looper的初始化和準(zhǔn)備工作已經(jīng)完成冕象,接下去就是常見的發(fā)送消息
<Handler.java>    
    // Message的復(fù)用機(jī)制下面再分析
    handler.sendMessage(Message.obtain());

    // 發(fā)送消息
    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

    // 發(fā)送消息如果不設(shè)置延時,內(nèi)部設(shè)置為0
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        // 時間的基數(shù)為當(dāng)前系統(tǒng)啟動時間汁蝶,即使delay=0渐扮,實際上uptimeMillis也是>0
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    // 不論是post论悴,sendEmptyMessage等最終都是走到這里
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;// 當(dāng)前線程消息隊列
        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(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        // target賦值給當(dāng)前handler,所以message持有當(dāng)前handler的引用墓律,一般情況new Handler匿名內(nèi)部類持有外部Activity引用膀估,而MessageQueue持有Message引用,從而產(chǎn)生生命周期比Activity長的引用鏈耻讽,因此會導(dǎo)致Activity內(nèi)存泄露(普通匿名內(nèi)部類一般不會導(dǎo)致內(nèi)存泄露)
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        // 如果是異步消息察纯,設(shè)置標(biāo)記位FLAG_ASYNCHRONOUS
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        // 最終調(diào)用MessageQueue的enqueueMessage方法,將當(dāng)前消息放到隊列的尾部
        return queue.enqueueMessage(msg, uptimeMillis);
    }
  • 接下去就是將消息放入消息隊列针肥,等待消息的輪詢處理
<MessageQueue.java>
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        // 注意饼记,同步鎖!為了處理多個線程中Handler發(fā)送消息的并發(fā)處理
        synchronized (this) {
            // 當(dāng)前Looper正在執(zhí)行quit操作慰枕,也是調(diào)用MessageQueue的quit具则,此時發(fā)送消息沒必要處理了
            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();// 標(biāo)記當(dāng)前消息正在處理
            msg.when = when;// 當(dāng)前消息的需處理時間
            // ----->>>> 重點:消息入隊及排序
            Message p = mMessages;// 當(dāng)前消息隊頭
            boolean needWake;// 是否需要喚醒,一般不需要用戶去主動去喚醒具帮,除非無消息了長期休眠
            // 1. p = null說明當(dāng)前隊列沒有消息博肋,直接插入到隊頭(通常這種場景)
            // 2. when==0只有Handler調(diào)用sendMessageAtFrontOfQueue時成立
            // 3. when < p.when說明當(dāng)前消息時間小于隊頭消息的時間,需要插到最前面
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;// msg插入到隊頭:msg->p->...
                mMessages = msg;// 刷新當(dāng)前隊頭
                needWake = mBlocked;// 上次取消息時蜂厅,隊列沒有消息了束昵,mBlocked=true并且掛起,所以此刻需要喚醒
            } else {// 當(dāng)前隊列中已經(jīng)存在其他未處理消息葛峻,mBlocked=false
                // 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();// 一般是false
                Message prev;// 定義上一個消息
                for (;;) {// 循環(huán)遍歷
                    prev = p;// 將當(dāng)前隊頭賦值給上個消息
                    p = p.next;// 將之前的隊頭的下一個消息賦值給p
                    // 1. msg1 -> msg2(假設(shè)當(dāng)前隊列中有兩個消息锹雏,msg1是當(dāng)前隊頭)
                    // 2. 定義一個變量p = msg1
                    // 3. 定義一個變量pre = p, 也就是pre = msg1
                    // 4. p = msg1, p.next = msg1.next = msg2, 所以p = msg2
                    // 5. 此時消息順序: pre -> p
                    // 6. 不斷循環(huán)上述操作,pre和p一直往后移動:pre是倒數(shù)第二個术奖,p是隊尾
                    if (p == null || when < p.when) {
                        // p==null說明已經(jīng)是隊列的尾部了
                        // when<p.when說明當(dāng)前消息處理時間比當(dāng)前隊尾的消息要早礁遵,需要排到p的前面
                        break;
                    }
                    // 一般needWake=false進(jìn)不來
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 上面循環(huán)排序后獲取到的pre是倒數(shù)第二個消息,p是最后一個消息
                // 將當(dāng)前發(fā)送的消息插入到當(dāng)前遍歷到的隊尾(p不一定是整個隊列最后的消息):
                // pre->msg->p || pre->msg->p->...
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                // native層喚醒采记,停止next方法中的阻塞nativePollOnce佣耐,立刻獲取消息
                nativeWake(mPtr);
            }
        }
        return true;
    }
  • 正常情況下至此,消息已經(jīng)發(fā)送并且成功加入到MessageQueue的隊尾了唧龄,接下去就是消息的輪詢處理了兼砖,上面已經(jīng)提到了Looper.loop()方法,內(nèi)部是個死循環(huán)既棺,不斷調(diào)用MessageQueue.next()去獲取下一個消息
<Looper.java>
        // 開啟死循環(huán)輪詢消息
        for (;;) {
            // 從消息隊列中獲取下一個未處理的消息讽挟,重點,后面分析
            Message msg = queue.next(); // might block
            // 消息隊列為空丸冕,沒有消息了耽梅,此時說明退出應(yīng)用了
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            ...
            try {
                // 消息分發(fā)回調(diào)給handler處理
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } 
            ...
            // 回收消息加入復(fù)用池
            msg.recycleUnchecked();
        }

<MessageQueue.java>
    // 獲取下一個消息
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;// native層的MessageQueue的指針
        if (ptr == 0) {
            return null;// native退出了或者掛了,epoll機(jī)制已經(jīng)無效了胖烛,還玩啥?
        }
        // 這個值會影響下面的mBlocked標(biāo)記
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        // 同樣是個死循環(huán)
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // 此處可能會阻塞眼姐,但是不會導(dǎo)致應(yīng)用卡死(應(yīng)用卡死是ANR)
            // 阻塞過程會休眠cpu(linux機(jī)制)诅迷,節(jié)約系統(tǒng)資源
            // 阻塞不會一直延續(xù),會在超時后自動喚醒众旗,touch事件或者調(diào)用nativeWait會主動喚醒休眠
            nativePollOnce(ptr, nextPollTimeoutMillis);
            // 注意罢杉,消息入隊同步鎖,取消息同樣有同步鎖贡歧,都是為了處理多線程并發(fā)問題
            // 此處也說明當(dāng)前隊列的消息并不是完全準(zhǔn)確的按照delayTime處理滩租,可能存在延時
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();// 當(dāng)前系統(tǒng)啟動時間
                Message prevMsg = null;// 定義上一個消息prevMsg
                Message msg = mMessages;// 當(dāng)前消息隊頭
                // msg.target==null說明是postSyncBarrier方式發(fā)送的同步屏障消息,非一般通過handler的sendMessage發(fā)送出來
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;// 不斷往后遍歷艘款,msg為當(dāng)前遍歷的隊尾,直到找到最前面的異步消息
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {// msg為當(dāng)前隊頭或者異步消息
                    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) {// 同步屏障消息的場景晌柬,msg為最靠前的異步消息
                            prevMsg.next = msg.next;// 取出了隊列中間最前的異步消息,重新鏈接隊列鏈表
                        } else {
                            mMessages = msg.next;// 之前的隊頭已經(jīng)取出來處理了郭脂,隊頭后移一個
                        }
                        msg.next = null;// msg已經(jīng)從隊列中取出
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();// 標(biāo)記msg正在使用
                        return msg;// 取出需要處理的消息年碘,返回
                    }
                } else {// msg為空,沒有消息了展鸡,需要一直掛起
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {// 正在退出屿衅,返回null
                    dispose();
                    return null;
                }

                // 假設(shè)上面沒有return message,說明當(dāng)前隊列沒有需要處理的消息(沒有消息或者需要處理的時間未到)莹弊,則開始執(zhí)行idleHandler
                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0 // 上面初始值為-1
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();// 獲取idleHandlers容量
                }
                if (pendingIdleHandlerCount <= 0) {// 沒有idleHandler需要執(zhí)行
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    // 走到這說明沒有return message涤久,也沒有idleHandler需要執(zhí)行,所以要阻塞
                    // 此時nextPollTimeoutMillis = -1會一直阻塞直到被主動喚醒
                    continue;
                }
                // 初始化idleHandlers
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
            // 執(zhí)行空閑idleHandler
            // 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();// 是否一次性的標(biāo)志忍弛,具體執(zhí)行
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);// 一次性執(zhí)行后移除
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;// 上面已經(jīng)執(zhí)行過了idleHandler响迂,所以賦值0,所以for死循環(huán)中不會再執(zhí)行

            // 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;// idleHandler執(zhí)行后立即喚醒细疚,可能有延時消息到了處理時機(jī)
        }
    }
  • 取出消息后蔗彤,再回去看Looper.loop()里對消息的分發(fā)
<Looper.java>
    public static void loop() {
        ...
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {// 沒消息說明當(dāng)前已經(jīng)調(diào)用了quit退出循環(huán)
                // No message indicates that the message queue is quitting.
                return;
            }

            ...

            try {
                msg.target.dispatchMessage(msg);// msg的target就是Handler,回調(diào)handler的dispatchMessage方法
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...

            msg.recycleUnchecked();
        }
    }

<Handler.java>
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);// 如果msg有自己的callback疯兼,執(zhí)行msg的callback然遏,一般情況下callback都為null
        } else {
            if (mCallback != null) {// 這個mCallback對于app來說是無法使用的,google已經(jīng)添加了注解    @UnsupportedAppUsage
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);// 所以吧彪,通常情況就走到這里了啦鸣,回調(diào)消息處理
        }
    }

    // Message.obtain方法有一個重載,內(nèi)部有兩個參數(shù):    
    // public static Message obtain(Handler h, Runnable callback) {...}
    private static void handleCallback(Message message) {
        message.callback.run();// 執(zhí)行callback的run方法
    }
  • 至此来氧,整個消息分發(fā)處理流程已經(jīng)分析結(jié)束诫给∠憷總結(jié)如下:
  1. Looper.prepare -> new Looper對象并添加到ThreadLocal保存
  2. Looper構(gòu)造函數(shù)中 -> new MessageQueue創(chuàng)建消息隊列
  3. Looper.loop -> 開啟死循環(huán)輪詢,不斷調(diào)用MessageQueue.next
  4. MessageQueue.next()獲取下一條需要處理的消息中狂,無消息或者還未到處理時間則阻塞休眠
  5. Handler.sendMessage -> Handler.enqueueMessage -> MessageQueue.enqueueMessage將消息添加到隊列
  6. Looper.loop循環(huán)中通過MessageQueue.next取出發(fā)送的消息 -> Msg
  7. Msg.target(Handler).dispatchMessage -> Handler.handleMessage
  8. 當(dāng)消息隊列沒有消息時會持續(xù)掛起凫碌,下一條消息來了會觸發(fā)主動喚醒
  9. 正常情況下系統(tǒng)會根據(jù)當(dāng)前時間和隊頭消息的處理時間計算出下次喚醒的時間,不需要主動觸發(fā)喚醒

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

  • Message是可以通過new的方式來進(jìn)行創(chuàng)建胃榕,但是該方式就會在堆區(qū)申請內(nèi)存空間盛险,android系統(tǒng)內(nèi)部充斥著大量消息,如果每個消息都通過該方式進(jìn)行創(chuàng)建勋又,該消息處理完后會產(chǎn)生大量的垃圾碎片苦掘,造成內(nèi)存抖動,頻繁gc楔壤,嚴(yán)重影響性能鹤啡。因此,內(nèi)部通過一個緩存池進(jìn)行復(fù)用(鏈表結(jié)構(gòu))
<Message.java>
    public static final Object sPoolSync = new Object();// 同步對象鎖
    private static Message sPool;// 消息隊列中的隊頭(也可以稱為緩存池蹲嚣,message自身是鏈表結(jié)構(gòu)递瑰,next指向下一條消息)
    private static int sPoolSize = 0;// 當(dāng)前緩存池大小
    private static final int MAX_POOL_SIZE = 50;// 最多緩存50個消息
    @UnsupportedAppUsage
    /*package*/ Message next;

    // 享元設(shè)計模式,對象共享隙畜,只是擦除內(nèi)部屬性
    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {// 同步處理
            if (sPool != null) {// 緩存池為空
                // 從message鏈表結(jié)構(gòu)中取出隊頭的message給外部使用抖部,同時將sPool指向新的隊頭
                Message m = sPool;
                sPool = m.next;
                m.next = null;// 擦除內(nèi)部標(biāo)記,斷開消息鏈
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;// 返回該消息對象
            }
        }
        return new Message();
    }

    // sPool緩存池的創(chuàng)建初始化议惰,系統(tǒng)調(diào)用
    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    @UnsupportedAppUsage
    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 = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {// 同步處理
            if (sPoolSize < MAX_POOL_SIZE) {// 當(dāng)前容量小于最大容量50
                next = sPool;// sPool賦值給next
                sPool = this;// 當(dāng)前消息賦值給sPool:sPool->當(dāng)前msg->next... 下次復(fù)用的永遠(yuǎn)是隊頭的消息
                sPoolSize++;// 容量+1
            }
        }
    }

Looper線程單例原理

核心類就是ThreadLocal慎颗,它提供線程局部變量,每個線程都有自己獨立的一份變量言询,通常是類中的 private static字段哗总,它們希望將狀態(tài)與某一個線程相關(guān)聯(lián),在多線程編程中常用倍试,比如Android的繪制同步機(jī)制Choreographer中也有使用讯屈。

<Looper.java>
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    // 會判斷,如果之前ThreadLocal已經(jīng)存在Looper對象县习,拋出異常涮母,一個線程只能有一個Looper
    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));
    }

<ThreadLocal.java>
    public void set(T value) {
        Thread t = Thread.currentThread();// 獲取當(dāng)前線程
        ThreadLocalMap map = getMap(t);// 獲取當(dāng)前線程的ThreadLocalMap,默認(rèn)為null
        if (map != null)// 當(dāng)前線程已經(jīng)初始化了ThreadLocalMap
            map.set(this, value);// key為當(dāng)前ThreadLoacl
        else
            createMap(t, value);// 延遲躁愿,第一次set時候才進(jìn)行初始化
    }

    void createMap(Thread t, T firstValue) {
        // ThreadLocalMap和當(dāng)前線程綁定叛本,保證線程唯一性
        t.threadLocals = new ThreadLocalMap(this, firstValue);// 創(chuàng)建ThreadLocalMap,并且添加初始值
    }

    static class ThreadLocalMap {
        // 構(gòu)造函數(shù)會存儲第一次賦值
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;// 存儲的值

            Entry(ThreadLocal<?> k, Object v) {
                super(k);// 弱引用緩存當(dāng)前ThreadLocal對象
                value = v;// 存儲的值
            }

            //Entry.get() == null表示key不再被引用彤钟,表示ThreadLocal對象被回收
        }

        private Entry[] table;// 緩存key-value的數(shù)組

        // setter賦值
        private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;// 緩存數(shù)組
            int len = tab.length;// 當(dāng)前容量
            int i = key.threadLocalHashCode & (len-1);// 生成索引来候,新增一個就會變化

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();
                if (k == key) {// 當(dāng)前ThreadLocal對象為key,如果相等覆蓋之前的value值
                    e.value = value;
                    return;
                }
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            tab[i] = new Entry(key, value);// 加入緩存
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

        // getter取值
        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);// 生成索引逸雹,規(guī)則同set方法
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
    }

    public T get() {
        Thread t = Thread.currentThread();// 獲取當(dāng)前線程
        ThreadLocalMap map = getMap(t);// ThreadLocalMap和線程Thread唯一對應(yīng)营搅,所以get操作只有當(dāng)前線程可以訪問
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);// 根據(jù)key獲取entry
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;// 取出value值
                return result;
            }
        }
        return setInitialValue();
    }

  • 小結(jié)一下set和get流程云挟,里面具體的hash映射算法和索引計算法未分析,看不懂(線性探測法尋址)
  1. ThreadLocal.set() -> getMap() or createMap()返回當(dāng)前ThreadThreadLocalMap
  2. 當(dāng)前ThreadThreadLocalMap.put(ThreadLocal, value)存入數(shù)據(jù)转质,其中key就是ThreadLoacal
  3. ThreadLocal.get() -> getMap()返回當(dāng)前ThreadThreadLocalMap
  4. 當(dāng)前ThreadThreadLocalMap.get(ThreadLocal)讀取數(shù)據(jù)
  • ThreadLocal使用不當(dāng)會出現(xiàn)內(nèi)存泄露园欣,出現(xiàn)內(nèi)存泄露需同時滿足以下三個條件:
  1. ThreadLocal引用被設(shè)置為null,且后面沒有set休蟹,get,remove操作沸枯,該entry變成游離狀態(tài)
  2. 線程一直運行,不停止(線程池)
  3. 觸發(fā)了垃圾回收(Minor GCFull GC
  • Android Looper中并沒有調(diào)用ThreadLocalremove赂弓,為何不會出現(xiàn)內(nèi)存泄露呢绑榴?主要有以下原因:
    Looper中的ThreadLocal使用static final修飾,static修飾的生命周期與Application同在盈魁,Application退出時線程自然停止運行了翔怎,并且final修飾其他地方無法修改其引用。因此同時打破了上面的條件1,2备埃,不會出現(xiàn)ThreadLocalMap存儲數(shù)組中keynull時觸發(fā)GC的內(nèi)存泄露問題

總結(jié)一下姓惑,由Handler引申出來的知識點包括以下:

  1. Handler發(fā)送消息流程褐奴,與Looper按脚,MessageQueue三者之間角色關(guān)系
  2. Handler內(nèi)存泄露原因以及處理方案
  3. MessageQueue數(shù)據(jù)結(jié)構(gòu),鏈表和隊列區(qū)別敦冬,Message入隊和出隊的遍歷方法
  4. Looper如何保證線程變量唯一辅搬,ThreadLocal原理和內(nèi)存泄露
  5. ThreadLocal內(nèi)存泄露引申出弱引用,軟引用和強(qiáng)引用
  6. Message消息的復(fù)用機(jī)制脖旱,緩存原理堪遂,鏈表結(jié)構(gòu)
  7. Message設(shè)計復(fù)用機(jī)制的原因,內(nèi)存抖動萌庆,享元設(shè)計模式
  8. 消息屏障Barrier是如何保證優(yōu)先執(zhí)行的溶褪,以及系統(tǒng)內(nèi)部應(yīng)用場景
  9. Android中應(yīng)用卡死的定義,ANR
  10. MessageQueue死循環(huán)節(jié)約資源處理方案践险,linuxePoll機(jī)制
  11. IdleHandler的作用猿妈,和處理時機(jī),處理方式
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巍虫,一起剝皮案震驚了整個濱河市彭则,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌占遥,老刑警劉巖俯抖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓦胎,居然都是意外死亡芬萍,警方通過查閱死者的電腦和手機(jī)尤揣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來担忧,“玉大人芹缔,你說我怎么就攤上這事∑渴ⅲ” “怎么了最欠?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惩猫。 經(jīng)常有香客問我芝硬,道長,這世上最難降的妖魔是什么轧房? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任拌阴,我火速辦了婚禮,結(jié)果婚禮上奶镶,老公的妹妹穿的比我還像新娘迟赃。我一直安慰自己,他們只是感情好厂镇,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布纤壁。 她就那樣靜靜地躺著,像睡著了一般捺信。 火紅的嫁衣襯著肌膚如雪酌媒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天迄靠,我揣著相機(jī)與錄音秒咨,去河邊找鬼。 笑死掌挚,一個胖子當(dāng)著我的面吹牛雨席,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吠式,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼陡厘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奇徒?” 一聲冷哼從身側(cè)響起雏亚,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎摩钙,沒想到半個月后罢低,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年网持,在試婚紗的時候發(fā)現(xiàn)自己被綠了宜岛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡功舀,死狀恐怖萍倡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辟汰,我是刑警寧澤列敲,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站帖汞,受9級特大地震影響戴而,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜翩蘸,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一所意、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧催首,春花似錦扶踊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涝滴,卻和暖如春绣版,著一層夾襖步出監(jiān)牢的瞬間胶台,已是汗流浹背歼疮。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留诈唬,地道東北人韩脏。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像铸磅,于是被迫代替她去往敵國和親赡矢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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