溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Java中Handler源碼的示例分析

發(fā)布時(shí)間:2021-12-30 16:56:19 來(lái)源:億速云 閱讀:116 作者:小新 欄目:編程語(yǔ)言

這篇文章主要介紹了Java中Handler源碼的示例分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

從很早開(kāi)始就認(rèn)識(shí)到 Handler 了,只不過(guò)那時(shí)修為尚淺,了解的不夠深刻,也沒(méi)有應(yīng)用自如。不過(guò)隨著工作時(shí)間的增長(zhǎng),對(duì) Handler 又有了更深層次的認(rèn)識(shí),于是有了這篇博客,希望盡可能的總結(jié)出多的知識(shí)點(diǎn)。 

Handler 在 Java 層源碼主要有 4 個(gè)類(lèi):Looper、MessageQueue、Message、Handler。我歸納了他們的幾個(gè)主要知識(shí)點(diǎn): 

  • Looper:sThreadLocal、Looper.loop();

  • Message:數(shù)據(jù)結(jié)構(gòu)、消息緩存池;

  • MessageQueue:enqueueMessage、next、管道等待、同步消息隔離、idleHandler;

  • Handler:send/post、dispatchMessage 消息處理優(yōu)先級(jí)。

Looper

Looper 數(shù)據(jù)結(jié)構(gòu)

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class

final MessageQueue mQueue;

// sThreadLocal
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) { throw Exception ... }
    sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

// sMainLooper
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) { throw Exception ...}
        sMainLooper = myLooper();
    }
}
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

// mQueue
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
}
  • sThreadLocal:靜態(tài)常量,保證一個(gè)線程只有一個(gè) Looper;

  • sMainLooper:靜態(tài)變量,在 prepareMainLooper 中賦值當(dāng)前線程 Looper;

  • mQueue:變量,Looper 構(gòu)造函數(shù)中初始化,因?yàn)橐粋€(gè)線程只有一個(gè) Looper,所以也同樣只有一個(gè) mQueue。

通過(guò)以上分析,我們可以總結(jié)出一下特性: 

  1. Looper、MessageQueue 是線程唯一的;

  2. 一個(gè)進(jìn)程只有一個(gè) sMainLooper;

  3. 根據(jù) ThreadLocal 的特性,可通過(guò) myLooper 方法獲取當(dāng)前線程的 Looper。

Looper.loop()

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) { 
        Message msg = queue.next();
        ...
        msg.target.dispatchMessage(msg);
        ...
        msg.recycleUnchecked();
    }
}

Looper.loop() 方法雖然看起來(lái)很多,其實(shí)他主要就做了三件事: 

  1. 從消息隊(duì)列中獲取下一個(gè)消息;

  2. msg.target 就是 handler,通過(guò) dispatchMessage 方法把消息分發(fā)下去,這個(gè)方法下面會(huì)有說(shuō)到;

  3. 消息回收,放到消息緩存池里。這里需要注意的是 Message 對(duì)象并沒(méi)有釋放,會(huì)緩存起來(lái)。

Message

Message 數(shù)據(jù)結(jié)構(gòu)

public int what, arg1, arg2;
public Object obj;
public Messenger replyTo;
int flags;
long when;      // 消息發(fā)送時(shí)間
Bundle data;
Handler target;
Runnable callback;

Message next;

private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = ;

private static final int MAX_POOL_SIZE = 50;
  • 看到 next 變量,我們會(huì)想到單鏈表,其實(shí) Message 就相當(dāng)于單鏈表的 node,MessageQueue 就是一個(gè)單鏈表了,會(huì)持有表頭的引用;

  • what、arg1、arg2、obj、data 就是我們發(fā)送的一些信息;

  • 值得注意的是 target,他是 Handler 類(lèi)型,就是本消息的 Handler,會(huì)在 Handler 發(fā)送消息的時(shí)候賦值;

  • 后面的四個(gè)對(duì)象,都是和消息緩存池有關(guān)的。

Message 消息緩存池

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = ; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

void recycleUnchecked() {
    flags = FLAG_IN_USE;
    what = ;
    arg1 = ;
    arg2 = ;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = ;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}
  • 事實(shí)上緩存池的數(shù)據(jù)結(jié)構(gòu)也是一個(gè)鏈表,sPool 為鏈表頭引用,最大容量為 50;

  • 回收消息時(shí),會(huì)把消息里面所有參數(shù)重置,并把當(dāng)前消息設(shè)為鏈表頭;

  • 獲取消息時(shí),返回當(dāng)前鏈表頭,并把 next 置空。

MessageQueue

插入隊(duì)列

boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when ==  || when < p.when) {
            // 作為表頭,如果隊(duì)列是阻塞狀態(tài)則需要喚醒
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 根據(jù)時(shí)間順序,插入鏈表中間
            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;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

主要作為插入隊(duì)列的方法,有下列幾個(gè)特性: 

  • 把消息加入消息隊(duì)列,如果當(dāng)前表頭為空,則把消息作為表頭引用;如果不為空,則會(huì)根據(jù)時(shí)間的順序,插入到對(duì)應(yīng)的時(shí)間中;

  • nativeWake 是調(diào)用底層在管道中寫(xiě)操作以喚醒,在隊(duì)列不是阻塞的狀態(tài)下是不需要喚醒的;

  • 另外注意其中用了 synchronized 關(guān)鍵字,說(shuō)明消息隊(duì)列的插入是線性安全的,刪除也是線性安全的,之后我們會(huì)說(shuō)到。

MessageQueue.next()

for (;;) {
    nativePollOnce(ptr, nextPollTimeoutMillis);
    synchronized (this) {
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
        if (msg != null && msg.target == null) {
            // 如果有同步消息隔離,則會(huì)優(yōu)先查找異步消息
            do {
                prevMsg = msg;
                msg = msg.next;
            } while (msg != null && !msg.isAsynchronous());
        }
        if (msg != null) {
            if (now < msg.when) {
                // 計(jì)算距離下一個(gè)消息的時(shí)間
                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 {
            // 沒(méi)有更多消息的時(shí)候,nextPollTimeoutMillis 會(huì)置為 1。
            nextPollTimeoutMillis = -1;
        }

        ...
    }

    // 如果目前沒(méi)有消息,已經(jīng)處在空閑狀態(tài),則執(zhí)行 idler.queueIdle
    for (int i = ; 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);
            }
        }
    }
    ...
}

此方法會(huì)從消息隊(duì)列中讀取下一個(gè)消息返回,主要做了以下操作: 

  • nativePollOnce 函數(shù)會(huì)調(diào)用底層管道操作函數(shù),nextPollTimeoutMillis 為 -1 時(shí),會(huì)阻塞,為 0 時(shí)不會(huì)阻塞,大于 0 時(shí),會(huì)阻塞相應(yīng)的時(shí)間;

  • 如果有同步消息隔離,則會(huì)優(yōu)先查找異步消息;

  • 獲取當(dāng)前時(shí)間隊(duì)列的消息,并返回;

  • 如果隊(duì)列沒(méi)有任何消息,則會(huì)執(zhí)行 idler.queueIdle,通知監(jiān)聽(tīng)者當(dāng)前隊(duì)列處于空閑狀態(tài)。

同步消息隔離

上面我們有提到了同步消息隔離,這里我們介紹一下。同步隔離,有時(shí)候也可以叫異步消息,說(shuō)的是一個(gè)意思。在源碼中主要用于優(yōu)先更新 UI。

private IdleHandler[] mPendingIdleHandlers;

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

private int postSyncBarrier(long when) {
    // 向消息隊(duì)列中加入一個(gè) handler 為空的消息
    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 != ) {
            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;
    }
}

如上 postSyncBarrier 函數(shù)中會(huì)向消息隊(duì)列中加入一個(gè) handler(即 Message 的 target) 為空的消息作為標(biāo)識(shí)。在我們上面 MessageQueue.next() 的函數(shù)中,當(dāng) msg.target == null 時(shí),會(huì)優(yōu)先獲取異步消息并返回。  
因此想要使用異步消息有兩個(gè)條件:

  1. 消息為異步消息,即 msg.isAsynchronous() 返回 false;

  2. 需要獲取當(dāng)前隊(duì)列并運(yùn)行 postSyncBarrier() 函數(shù)。

IdleHandler

Handler 還提供了消息隊(duì)列空閑狀態(tài)通知。

private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();

public void addIdleHandler(@NonNull IdleHandler handler) {
    if (handler == null) {
        throw new NullPointerException("Can't add a null IdleHandler");
    }
    synchronized (this) {
        mIdleHandlers.add(handler);
    }
}

public void removeIdleHandler(@NonNull IdleHandler handler) {
    synchronized (this) {
        mIdleHandlers.remove(handler);
    }
}

IdleHandler 的源碼比較簡(jiǎn)單,就是一個(gè) ArrayList,然后進(jìn)行增加刪除操作。注意,這個(gè)也是線性安全的。

Handler

post/sendMessage

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

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, );
}

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

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
  • sendMessage 和 post 最本質(zhì)的區(qū)別是之后處理任務(wù)時(shí)的優(yōu)先級(jí),post 會(huì)處理 Runnable 中的任務(wù),而 sendMessage 會(huì)回調(diào)給 handler 處理;

  • 他們最終都會(huì)走 enqueueMessage 方法,并設(shè)置當(dāng)前 Handler 為 msg.target。

dispatchMessage

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

任務(wù)執(zhí)行時(shí)就會(huì)運(yùn)行這個(gè)函數(shù),主要是一個(gè)優(yōu)先級(jí)的問(wèn)題:

  1. callback 優(yōu)先級(jí)最高,也就是 post 發(fā)送的消息

  2. mCallback.handleMessage(msg),優(yōu)先級(jí)第二

  3. handleMessage(msg),優(yōu)先級(jí)第三

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Java中Handler源碼的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI