天天看點

開發者營地 | Android消息機制詳解

一、Handler基礎

1. Handler簡介

在Android中使用消息機制,通常就是指Handler機制。Handler是Android消息機制的上層接口。

Handler的使用過程很簡單,通過它可以輕松地将一個任務切換到Handler所在的線程中去執行。通常情況下,Handler的使用場景就是更新UI。

示例:

public class Activity extends android.app.Activity {
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            System.out.println(msg.what);
        }
    };
    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                //...耗時操作
                Message message = Message.obtain();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        }).start();
    }
}
           

在子線程中,進行耗時操作,執行完操作後,發送消息,通知主線程更新UI。這便是消息機制的典型應用場景。

2. Handler模型

Android消息機制中的五大概念:

  • ThreadLocal

    :目前線程存儲的資料僅能從目前線程取出。
  • MessageQueue

    :具有時間優先級的消息隊列(單連結清單)。因為單連結清單在插入和删除上比較有優勢。主要功能向消息池投遞消息

    MessageQueue.enqueueMessage

    和取走消息池的消息

    MessageQueue.next

  • Looper

    :儲存在ThreadLocal中的輪詢消息隊列,不斷循環執行

    Looper.loop

    ,若有新的消息到來,從MessageQueue中讀取消息,按分發機制将消息分發給目标處理者。
  • Handler

    :具體處理邏輯的地方,主要功能向消息池發送各種消息事件

    Handler.sendMessage

    和處理相應消息事件

    Handler.handleMessage

  • Message

    :需要傳遞的消息,可以傳遞資料。
開發者營地 | Android消息機制詳解

3. Handler架構

消息機制的運作流程:

在子線程執行完耗時操作,當Handler發送消息時,将會調用

MessageQueue.enqueueMessage

,向消息隊列中添加消息。當通過

Looper.loop

開啟循環後,會不斷地從線程池中讀取消息,即調用

MessageQueue.next

,然後調用目标Handler(即發送該消息的Handler)的

dispatchMessage

方法傳遞消息,然後傳回到Handler所線上程,目标Handler收到消息,調用

handleMessage

方法,接收消息,處理消息。

開發者營地 | Android消息機制詳解

MessageQueue,Handler和Looper三者之間的關系:

每個線程中隻能存在一個Looper,Looper是儲存在ThreadLocal中的。

主線程(UI線程)已經建立了一個Looper,是以在主線程中不需要再建立Looper,但是在其他線程中需要建立Looper。

每個線程中可以有多個Handler,即一個Looper可以處理來自多個Handler的消息。

Looper中維護一個MessageQueue,來維護消息隊列,消息隊列中的Message可以來自不同的Handler。

開發者營地 | Android消息機制詳解

下面是消息機制的整體架構圖,接下來我們将慢慢解剖整個架構。

開發者營地 | Android消息機制詳解

從中我們可以看出:

Looper有一個MessageQueue消息隊列;

MessageQueue有一組待處理的Message;

Message中記錄發送和處理消息的Handler;

Handler中有Looper和MessageQueue。

接下來我們通過源碼來分析Handler的原理。

二、Handler源碼

1. Looper

要想使用消息機制,首先要建立一個Looper。

1.1 初始化Looper

1.1.1 普通線程初始化

相關代碼如下:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

	final MessageQueue mQueue;
	final Thread mThread;	

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

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

prepare()

無參情況下,預設調用

prepare(true)

表示的是這個Looper可以退出,而對于false的情況則表示目前Looper不可以退出。

sThreadLocal.get() != null

這裡看出,隻能建立一個,不能重複建立Looper,否則會抛出RuntimeException。

然後建立Looper,并儲存在ThreadLocal。

其中ThreadLocal是線程本地存儲區(Thread Local Storage,簡稱為TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能通路對方的TLS區域。

Looper中建立了MessageQueue,且儲存了MessageQueue的引用。

// True if the message queue can be quit.
    private final boolean mQuitAllowed;	
	private long mPtr; // used by native code
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

	private native static long nativeInit();
           

MessageQueue的初始化過程中儲存了是否能退出這一狀态,且調用了

nativeInit()

這一native層的函數,儲存傳回的指針引用。

1.1.2 主線程初始化

主線程中不需要自己建立Looper,這是由于在程式啟動的時候,系統已經幫我們自動調用了

Looper.prepare()

方法。檢視ActivityThread中的

main()

方法,代碼如下所示:

public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
        	sMainThreadHandler = thread.getHandler();
        }
        
        Looper.loop();
        ...
    }
           

Android主線程的

Looper#prepareMainLooper()

方法,我們看下其實作。

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

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

    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
           

這裡首先也是調用了

prepare(false)

方法,false表示目前Looper不可以退出,主線程的Handler是不允許退出的。

Looper.loop()

是個死循環,後面的代碼正常情況不會執行。因為主線程不允許退出,退出就意味 APP 要挂。

通過

myLooper()

方法擷取主線程的Looper,指派給sMainLooper。

我們注意到

Looper#getMainLooper()

方法供我們擷取主線程的Looper。

1.2 開啟Looper

public static void loop() {
    final Looper me = myLooper();  //擷取TLS存儲的Looper對象 
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;  //擷取Looper對象中的消息隊列

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) { //進入loop的主循環方法
        Message msg = queue.next(); //可能會阻塞,因為next()方法可能會無限循環
        if (msg == null) { //消息為空,則退出循環
            return;
        }

        Printer logging = me.mLogging;  //預設為null,可通過setMessageLogging()方法來指定輸出,用于debug功能
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
		
        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        
        try {
            msg.target.dispatchMessage(msg);//擷取msg的目标Handler,然後用于分發Message 
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (slowDispatchThresholdMs > 0) {
            final long time = end - start;
            if (time > slowDispatchThresholdMs) {
                Slog.w(TAG, "...“);
            }
        }        

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

        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
         	Log.wtf(TAG, "...”);
        }
        msg.recycleUnchecked(); 
    }
}
           

loop()

進入循環模式,不斷重複下面的操作,直到消息為空時退出循環:

  • 讀取MessageQueue的下一條Message(關于next(),後面詳細介紹);
  • 把Message分發給相應的target(Handler)。

當next()取出下一條消息時,隊列中已經沒有消息時,next()會無限循環,産生阻塞。等待MessageQueue中加入消息,然後重新喚醒。

1.3 結束Looper

public void quit() {
        mQueue.quit(false);
    }

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

    void quit(boolean safe) {
        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);
        }
    }

    private native static void nativeWake(long ptr);
           

可以看到

quit()

quitSafely()

對應立即結束和安全結束方法。

特别地,對于主線程,在App退出時,ActivityThread中的 mH(Handler)收到消息後,才會執行退出。

//ActivityThread.java
case EXIT_APPLICATION:
    if (mInitialApplication != null) {
        mInitialApplication.onTerminate();
    }
    Looper.myLooper().quit();
    break;
           

手動退出主線程Looper,便會抛出異常:

Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.
           

主線程不允許退出,一旦退出就意味着程式挂了。

2. Handler

2.1 建立Handler

final Looper mLooper;
    final MessageQueue mQueue;
    final Callback mCallback;
    final boolean mAsynchronous;

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
        ...
        //必須先執行Looper.prepare(),才能擷取Looper對象,否則為null.
        mLooper = Looper.myLooper();  //從目前線程的TLS中擷取Looper對象
        if (mLooper == null) {
            throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue; //消息隊列,來自Looper對象
        mCallback = callback;  //回調方法,預設為null
        mAsynchronous = async; //設定消息是否為異步處理方式
    }

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

對于Handler的無參構造方法,預設采用目前線程TLS中的Looper對象,并且callback回調方法為null,且消息為同步處理方式。隻要執行的

Looper.prepare()

方法,那麼便可以擷取有效的Looper對象。

2.2 其他構造方法

public Handler() {
        this(null, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

       public Handler(Callback callback) {
        this(callback, false);
    } 

    public Handler(Callback callback, boolean async){
        ...//同上
    }
           

以上,預設采用目前線程的Looper建構Handler。

如果要指定Looper,有如下構造方法。

public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

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

3. 發送消息

發送消息有幾種方式,但是歸根結底都是調用了

sendMessageAtTime()

方法。

在子線程中通過Handler的post()方式或send()方式發送消息,最終都是調用了

sendMessageAtTime()

方法。

3.1 post方法

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

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

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

3.2 send方法

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    } 
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
           

3.3 runOnUiThread()

就連子線程中調用Activity中的runOnUiThread()中更新UI,其實也是發送消息通知主線程更新UI,最終也會調用

sendMessageAtTime()

方法。

public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
           

如果目前的線程不等于UI線程(主線程),就去調用Handler的post()方法,最終會調用

sendMessageAtTime()

方法。否則就直接調用Runnable對象的run()方法。

3.4 sendMessageAtTime()

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
       //其中mQueue是消息隊列,從Looper中擷取的
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        //調用enqueueMessage方法
        return enqueueMessage(queue, msg, uptimeMillis);
    }
           
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //調用MessageQueue的enqueueMessage方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }
           

可以看到sendMessageAtTime()`方法的作用很簡單,就是調用MessageQueue的enqueueMessage()方法,往消息隊列中添加一個消息。

下面來看enqueueMessage()方法的具體執行邏輯。

3.5 enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
    // 每一個Message必須有一個target
    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.");
    }
    synchronized (this) {
        if (mQuitting) {  //正在退出時,回收msg,加入到消息池
            ...
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            //p為null(代表MessageQueue沒有消息) 或者msg的觸發時間是隊列中最早的, 則進入該該分支
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; 
        } else {
            //将消息按時間順序插入到MessageQueue。一般地,不需要喚醒事件隊列,除非
            //消息隊頭存在barrier,并且同時Message是隊列中最早的異步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
           

MessageQueue是按照Message觸發時間的先後順序排列的,隊頭的消息是将要最早觸發的消息。

當有消息需要加入消息隊列時,會從隊列頭開始周遊,直到找到消息應該插入的合适位置,以保證所有消息的時間順序。

4. 擷取消息

當發送了消息後,在MessageQueue維護了消息隊列,然後在Looper中通過

loop()

方法,不斷地擷取消息。上面對

loop()

方法進行了介紹,其中最重要的是調用了

queue.next()

方法,通過該方法來提取下一條資訊。下面我們來看一下

next()

方法的具體流程。

4.1 next()

private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //當消息循環已經退出,則直接傳回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循環疊代的首次為-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        
        //阻塞操作,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會傳回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //當消息Handler為空時,查詢MessageQueue中的下一條異步消息msg,為空則退出循環。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            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;
                    //設定消息的使用狀态,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地擷取MessageQueue中的下一條即将要執行的消息
                }
            } else {
                //沒有消息
                nextPollTimeoutMillis = -1;
            }
         	//消息正在退出,傳回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //IdleHandler的處理
            ......
    }
}
           

nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一個消息到來前,還需要等待的時長;當nextPollTimeoutMillis = -1時,表示消息隊列中無消息,會一直等待下去。

可以看出

next()

方法根據消息的觸發時間,擷取下一條需要執行的消息,隊列中消息為空時,則會進行阻塞操作。

5. 分發消息

在loop()方法中,擷取到下一條消息後,執行

msg.target.dispatchMessage(msg)

,來分發消息到目标Handler對象。 下面就來具體看下

dispatchMessage(msg)

方法的執行流程。

5.1 dispatchMessage()

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //當Message存在回調方法,回調msg.callback.run()方法;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //當Handler存在Callback成員變量時,回調方法handleMessage();
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回調方法handleMessage()
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
   message.callback.run();
}
           

分發消息流程 :

  • 當Message的

    msg.callback

    不為空時,則回調方法

    msg.callback.run()

    ;
  • 當Handler的

    mCallback

    不為空時,則回調方法

    mCallback.handleMessage(msg)

  • 最後調用Handler自身的回調方法

    handleMessage()

    ,該方法預設為空,Handler子類通過覆寫該方法來完成具體的邏輯。

消息分發的優先級 :

  • Message的回調方法:

    message.callback.run()

    ,優先級最高;
  • Handler中Callback的回調方法:

    Handler.mCallback.handleMessage(msg)

    ,優先級僅次于1;
  • Handler的預設方法:

    Handler.handleMessage(msg)

    ,優先級最低。
  • 對于很多情況下,消息分發後的處理方法是第3種情況,即

    Handler.handleMessage()

    ,一般地往往通過覆寫該方法進而實作自己的業務邏輯。

6. 移除消息

removeAllMessagesLocked(),實作如下:

private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }
           

立即删除消息,不管消息是否在目前時間應該執行。

removeAllFutureMessagesLocked(),實作如下:

private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
           

等待目前時間應該執行的消息執行完成後删除所有消息。

removeCallbacksAndMessages(),實作如下:

//Handler
	public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

	//MessageQueue
    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }
           

移除所有的回調和消息。

7. 總結

以上便是消息機制的原理,以及從源碼角度來解析消息機制的運作過程。可以簡單地用下圖來了解。

開發者營地 | Android消息機制詳解

三、IdleHandler

IdleHandler 可以用來提升性能,主要用在我們希望能夠在目前線程消息隊列空閑時做些事情,不過最好不要做耗時操作。具體用法如下。

//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {  
    @Override  
    public boolean queueIdle() {  
        //你要處理的事情
        return false;    
    }  
});
           

關于 IdleHandler 在 MessageQueue 與 Looper 和 Handler 的關系原理源碼分析如下:

/**
 * 擷取目前線程隊列使用Looper.myQueue(),擷取主線程隊列可用getMainLooper().myQueue()
 */
public final class MessageQueue {
    ......
	Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    
    /**
     * 目前隊列将進入阻塞等待消息時調用該接口回調,即隊列空閑
     */
    public static interface IdleHandler {
        /**
         * 傳回true就是單次回調後不删除,下次進入空閑時繼續回調該方法,false隻回調單次。
         */
        boolean queueIdle();
    }

    /**
     * 判斷目前隊列是不是空閑的,輔助方法
     */
    public boolean isIdle() {
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            return mMessages == null || now < mMessages.when;
        }
    }

    /**
     * 添加一個IdleHandler到隊列,如果IdleHandler接口方法傳回false則執行完會自動删除,
     * 否則需要手動removeIdleHandler。
     */
    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    /**
     * <p>This method is safe to call from any thread.
     * 删除一個之前添加的 IdleHandler。
     */
    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }
    ......
    //Looper的prepare()方法會通過ThreadLocal準備目前線程的MessageQueue執行個體,
    //然後在loop()方法中死循環調用目前隊列的next()方法擷取Message。
    Message next() {
        ......
        for (;;) {
            ......
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                ......
                //把通過addIdleHandler添加的IdleHandler轉成數組存起來在mPendingIdleHandlers中
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }

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

            //循環周遊所有IdleHandler
            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 {
                    //調用IdleHandler接口的queueIdle方法并擷取傳回值。
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                //如果IdleHandler接口的queueIdle方法傳回false說明隻執行一次需要删除。
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            ......
        }
    }
}
           

四、同步屏障機制

Message分為3中:普通消息(同步消息)、屏障消息(同步屏障)和異步消息。

我們通常使用的都是普通消息,而屏障消息就是在消息隊列中插入一個屏障,在屏障之後的所有普通消息都會被擋着,不能被處理。不過異步消息卻例外,屏障不會擋住異步消息,是以可以這樣認為:屏障消息就是為了確定異步消息的優先級,設定了屏障後,隻能處理其後的異步消息,同步消息會被擋住,除非撤銷屏障。

開發者營地 | Android消息機制詳解

1. 插入屏障消息

同步屏障是通過

MessageQueue#postSyncBarrier

方法插入到消息隊列的。

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

 private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //1、屏障消息和普通消息的差別是屏障消息沒有tartget。
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            //2、根據時間順序将屏障插入到消息連結清單中适當的位置
            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;
            }
            //3、傳回一個序号,通過這個序号可以撤銷屏障
            return token;
        }
    }
           

postSyncBarrier方法就是用來插入一個屏障到消息隊列的,可以看到它很簡單,從這個方法我們可以知道如下:

  • 屏障消息和普通消息的差別在于屏障沒有tartget,普通消息有target是因為它需要将消息分發給對應的target,而屏障不需要被分發,它就是用來擋住普通消息來保證異步消息優先處理的。
  • 屏障和普通消息一樣可以根據時間來插入到消息隊列中的适當位置,并且隻會擋住它後面的同步消息的分發。
  • postSyncBarrier傳回一個int類型的數值,通過這個數值可以撤銷屏障。
  • postSyncBarrier方法是私有的,如果我們想調用它就得使用反射。
  • 插入普通消息會喚醒消息隊列,但是插入屏障不會。

2. 屏障消息的工作原理

通過postSyncBarrier方法屏障就被插入到消息隊列中了,那麼屏障是如何擋住普通消息隻允許異步消息通過的呢?我們知道MessageQueue是通過next方法來擷取消息的。

Message next() {
			//1、如果有消息被插入到消息隊列或者逾時時間到,就被喚醒,否則阻塞在這。
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {        
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {//2、遇到屏障  msg.target == null
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());//3、周遊消息連結清單找到最近的一條異步消息
                }
                if (msg != null) {
                	//4、如果找到異步消息
                    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;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 如果沒有異步消息就一直休眠,等待被喚醒。
                    nextPollTimeoutMillis = -1;
                }
			//。。。。
        }
    }
           

可以看到,在注釋2如果碰到屏障就周遊整個消息連結清單找到最近的一條異步消息,在周遊的過程中隻有異步消息才會被處理執行到 if (msg != null){}中的代碼。可以看到通過這種方式就擋住了所有的普通消息。

3. 發送異步消息

Handler有幾個構造方法,可以傳入async标志為true,這樣構造的Handler發送的消息就是異步消息。不過可以看到,這些構造函數都是hide的,正常我們是不能調用的,不過利用反射機制可以使用@hide方法。

/**
      * @hide
      */
    public Handler(boolean async) {}

    /**
     * @hide
     */
    public Handler(Callback callback, boolean async) { }

    /**
     * @hide
     */
    public Handler(Looper looper, Callback callback, boolean async) {}
           

當調用

handler.sendMessage(msg)

發送消息,最終會走到:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);//把消息設定為異步消息
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }
           

可以看到如果這個handler的mAsynchronous為true就把消息設定為異步消息,設定異步消息其實也就是設定msg内部的一個标志。而這個mAsynchronous就是構造handler時傳入的async。除此之外,還有一個公開的方法:

Message message=Message.obtain();
        message.setAsynchronous(true);
        handler.sendMessage(message);
           

在發送消息時通過

message.setAsynchronous(true)

将消息設為異步的,這個方法是公開的,我們可以正常使用。

4. 移除屏障

移除屏障可以通過MessageQueue的removeSyncBarrier方法:

//注釋已經寫的很清楚了,就是通過插入同步屏障時傳回的token 來移除屏障
	/**
     * Removes a synchronization barrier.
     *
     * @param token The synchronization barrier token that was returned by
     * {@link #postSyncBarrier}.
     *
     * @throws IllegalStateException if the barrier was not found.
     *
     * @hide
     */
    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;
            //找到token對應的屏障
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            final boolean needWake;
            //從消息連結清單中移除
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            //回收這個Message到對象池中。
            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);//喚醒消息隊列
            }
    }
           

5. 實戰

測試代碼如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Handler handler;
    private int token;

    public static final int MESSAGE_TYPE_SYNC=1;
    public static final int MESSAGE_TYPE_ASYN=2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initHandler();
        initListener();
    }

    private void initHandler() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler=new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.what == MESSAGE_TYPE_SYNC){
                            Log.d("MainActivity","收到普通消息");
                        }else if (msg.what == MESSAGE_TYPE_ASYN){
                            Log.d("MainActivity","收到異步消息");
                        }
                    }
                };
                Looper.loop();
            }
        }).start();
    }

    private void initListener() {
        findViewById(R.id.btn_postSyncBarrier).setOnClickListener(this);
        findViewById(R.id.btn_removeSyncBarrier).setOnClickListener(this);
        findViewById(R.id.btn_postSyncMessage).setOnClickListener(this);
        findViewById(R.id.btn_postAsynMessage).setOnClickListener(this);
    }

    //往消息隊列插入同步屏障
    @RequiresApi(api = Build.VERSION_CODES.M)
    public void sendSyncBarrier(){
        try {
            Log.d("MainActivity","插入同步屏障");
            MessageQueue queue=handler.getLooper().getQueue();
            Method method=MessageQueue.class.getDeclaredMethod("postSyncBarrier");
            method.setAccessible(true);
            token= (int) method.invoke(queue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //移除屏障
    @RequiresApi(api = Build.VERSION_CODES.M)
    public void removeSyncBarrier(){
        try {
            Log.d("MainActivity","移除屏障");
            MessageQueue queue=handler.getLooper().getQueue();
            Method method=MessageQueue.class.getDeclaredMethod("removeSyncBarrier",int.class);
            method.setAccessible(true);
            method.invoke(queue,token);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //往消息隊列插入普通消息
    public void sendSyncMessage(){
        Log.d("MainActivity","插入普通消息");
        Message message= Message.obtain();
        message.what=MESSAGE_TYPE_SYNC;
        handler.sendMessageDelayed(message,1000);
    }

    //往消息隊列插入異步消息
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
    private void sendAsynMessage() {
        Log.d("MainActivity","插入異步消息");
        Message message=Message.obtain();
        message.what=MESSAGE_TYPE_ASYN;
        message.setAsynchronous(true);
        handler.sendMessageDelayed(message,1000);
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onClick(View v) {
        int id=v.getId();
        if (id == R.id.btn_postSyncBarrier) {
            sendSyncBarrier();
        }else if (id == R.id.btn_removeSyncBarrier) {
            removeSyncBarrier();
        }else if (id == R.id.btn_postSyncMessage) {
            sendSyncMessage();
        }else if (id == R.id.btn_postAsynMessage){
            sendAsynMessage();
        }
    }
    
}
           

五、Handler擴充

1. 記憶體洩漏

Handler 允許我們發送延時消息,如果在延時期間使用者關閉了 Activity,那麼該 Activity 會洩露。

這個洩露是因為 Message 會持有 Handler,而又因為 Java 的特性,内部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就導緻 Activity 洩露。

解決該問題的最有效的方法是:将 Handler 定義成靜态的内部類,在内部持有 Activity 的弱引用,并及時移除所有消息。

private static class SafeHandler extends Handler {

    private WeakReference<HandlerActivity> ref;

    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}
           

并且再在

Activity.onDestroy()

前移除消息,加一層保障:

@Override
protected void onDestroy() {
  safeHandler.removeCallbacksAndMessages(null);
  super.onDestroy();
}
           

注意:單純的在

onDestroy

移除消息并不保險,因為

onDestroy

并不一定執行。

2. 建立 Message 執行個體的最佳方式

由于 Handler 極為常用,是以為了節省開銷,Android 給 Message 設計了回收機制,是以我們在使用的時候盡量複用 Message ,減少記憶體消耗。

  • 通過 Message 的靜态方法

    Message.obtain()

    擷取,源碼如下:
private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;

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

   public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }


    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }

    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;

        return m;
    }

    public static Message obtain(Handler h, int what,
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }
           

從sPool中取一個Message,複制後傳回。

  • 通過 Handler 的公有方法

    handler.obtainMessage()

    ,源碼如下:
public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

    public final Message obtainMessage(int what)
    {
        return Message.obtain(this, what);
    }
    
    public final Message obtainMessage(int what, Object obj)
    {
        return Message.obtain(this, what, obj);
    }

    public final Message obtainMessage(int what, int arg1, int arg2)
    {
        return Message.obtain(this, what, arg1, arg2);
    }
    
    public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
    {
        return Message.obtain(this, what, arg1, arg2, obj);
    }
           

可以看到是其實就是第一個方式的封裝。

3. 子線程使用Handler

示例代碼:

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_three);
        new Thread(new Runnable() {
            @Override
            public void run() {
                //建立Looper,MessageQueue
                Looper.prepare();
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(HandlerActivity.this,"toast",Toast.LENGTH_LONG).show();
                    }
                });
                //開始處理消息
                Looper.loop();
            }
        }).start();
    }

           

添加Looper.prepare()建立Looper,同時調用Looper.loop()方法開始處理消息。

在所有事情處理完成後應該調用quit方法來終止消息循環,否則這個子線程就會一直處于循環等待的狀态,是以不需要的時候終止Looper,調用

Looper.myLooper().quit()

4. 妙用 Looper 機制

我們可以利用 Looper 的機制來幫助我們做一些事情:

  • 将 Runnable post 到主線程執行;
  • 利用 Looper 判斷目前線程是否是主線程。

示例代碼如下:

public final class MainThread {

    private MainThread() {
    }

    private static final Handler HANDLER = new Handler(Looper.getMainLooper());
	
    //将 Runnable post 到主線程執行
    public static void run(@NonNull Runnable runnable) {
        if (isMainThread()) {
            runnable.run();
        }else{
            HANDLER.post(runnable);
        }
    }

    public static boolean isMainThread() {
        //利用 Looper 判斷目前線程是否是主線程。
        return Looper.myLooper() == Looper.getMainLooper();
    }

}
           

5. epoll機制

Looper會線上程中不斷的檢索消息,如果是子線程的Looper死循環,一旦任務完成,使用者應該手動退出,而不是讓其一直休眠等待。而對于主線程,我們是絕不希望會被運作一段時間自己就退出,是以通過死循環來保證不會被退出。

主線程的死循環并不會特别消耗 CPU 資源。這就涉及到 Linux pipe/epoll機制:簡單說就是在主線程的 MessageQueue 沒有消息時,便阻塞在 loop 的

queue.next()

中的

nativePollOnce(

) 方法裡,此時主線程會釋放 CPU 資源進入休眠狀态,直到下個消息到達或者有事務發生,通過往 pipe 管道寫端寫入資料來喚醒主線程工作。

這裡采用的 epoll 機制,是一種IO多路複用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程式進行讀或寫操作,本質同步I/O,即讀寫是阻塞的。 是以說,主線程大多數時候都是處于休眠狀态,并不會消耗大量CPU資源。

聊聊IO多路複用之select,poll,epoll詳解

6. ThreadLocal

ThreadLocal是一個線程内部的資料存儲類,通過它可以在指定線程存儲資料,資料存儲後,隻能在指定的線程可以擷取到存儲的資料,對于其他線程則無法擷取到資料。一般來說,當資料是以線程作為作用域并且不同線程有不同副本的時候,就可以考慮使用ThreadLocal。

Java中的實作是下面這樣,Java 的實作裡面也有一個 Map,叫做 ThreadLocalMap,不過持有 ThreadLocalMap 的不是 ThreadLocal,而是 Thread。Thread 這個類内部有一個私有屬性 threadLocals,其類型就是 ThreadLocalMap,ThreadLocalMap 的 Key 是 ThreadLocal。

開發者營地 | Android消息機制詳解

6.1 整體

精簡之後的代碼如下:

class Thread {
    //内部持有ThreadLocalMap
    ThreadLocal.ThreadLocalMap threadLocals;
}
class ThreadLocal<T>{
    public T get() {
        //首先擷取線程持有的ThreadLocalMap
        ThreadLocalMap map = Thread.currentThread().threadLocals;
        //在ThreadLocalMap中查找變量
        Entry e = map.getEntry(this);
        return e.value;  
    }
    
	public void set(T value) {
        //首先擷取線程持有的ThreadLocalMap
        ThreadLocalMap map = Thread.currentThread().threadLocals;
        //在ThreadLocalMap中設定變量
        if (map != null)
            map.set(this, value);
        else
            //建立ThreadLocalMap
            createMap(t, value);
    }
    
  static class ThreadLocalMap{
    //内部是數組而不是Map
    Entry[] table;
    //根據ThreadLocal查找Entry
    Entry getEntry(ThreadLocal key){
      //省略查找邏輯
    }
    //Entry定義
    static class Entry extends WeakReference<ThreadLocal<?>>{
		Object value;
		Entry(ThreadLocal<?> k, Object v) {
			super(k);
            value = v;
		}
    }
  }
}
           

6.2 get

//ThreadLocal
	public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

   ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

	//ThreadLocal.ThreadLocalMap
    private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);
    }
           

6.3 set

//ThreadLocal
	public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

   ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

	//ThreadLocal.ThreadLocalMap
	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);
    }

    private void set(ThreadLocal<?> key, Object value) {

        Entry[] tab = table;
        int len = tab.length;
        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) {
                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();
    }

           

在Java的實作方案中,ThreadLocal僅僅隻是一個代理工具類,内部并不持有任何線程相關的資料,所有和線程相關的資料都存儲在Thread裡。

這樣的設計從資料的親緣性上來講,ThreadLocalMap屬于Thread也更加合理。是以ThreadLocal的get方法,其實就是拿到每個線程獨有的ThreadLocalMap。

還有一個原因,就是不容易産生記憶體洩漏。Thread持有ThreadLocalMap,而且ThreadLocalMap裡對ThreadLocal的引用還是弱引用,是以隻要Thread對象可以被回收,那麼ThreadLocalMap就能被回收。Java的實作方案雖然看上去複雜一些,但是更安全。

但是,如果線上程池中使用ThreadLocal可能會導緻記憶體洩漏,原因是線程池中線程的存活時間太長,往往和程式都是同生共死的,這就意味着Thread持有的ThreadLocalMap一直都不會被回收,再加上ThreadLocalMap中的Entry對ThreadLocal是弱引用,是以隻要ThreadLocal結束了自己的生命周期是可以被回收掉的。但是Entry中的Value卻是被Entry強引用的,是以即便Value的生命周期結束了,Value也是無法被回收的,進而導緻記憶體洩漏。

是以我們可以通過

try{} finally{}

方案來手動釋放資源。

ExecutorService pools;
    ThreadLocal tl;

    pools.execute(()->{
      //ThreadLocal增加變量
      tl.set(obj);
      try {
        // 省略業務邏輯代碼
      }finally {
        //手動清理ThreadLocal 
        tl.remove();
      }
    });
           

7. Handler鎖相關

enqueueMessage()

通過synchronized關鍵字保證線程安全。同時

messagequeue.next()

内部也會通過synchronized加鎖,確定取的時候線程安全,同時插入也會加鎖。

我的學習筆記,歡迎star和fork

歡迎關注我的公衆号,持續分析優質技術文章

開發者營地 | Android消息機制詳解