天天看點

Android Handler線程通信機制詳解及手動實作閑扯概述HandlerMessage與MessageQueueMessageQueueLooper總結自己實作一個Handler

Android Handler線程通信機制詳解及手動實作

  • 閑扯
  • 概述
  • Handler
  • Message與MessageQueue
    • Message
      • Message的種類
  • MessageQueue
      • IdleHandler
  • Looper
  • 總結
  • 自己實作一個Handler
      • Handler
      • Looper
      • Message
      • MessageQueue
      • 使用

閑扯

        -很多類比如AsynTask之類已經對Handler實作了封裝,我不用了解Handler的機制,我也可以很快樂,那我還用了解handler嗎?

        -了解了Handler,你可以更快樂。

        -不是有那啥EventBus很屌嗎,老東西你的Handler最沒用了。

        -沒有最強的code,隻有最強的coder。解耦不一定就好過耦合,Handler仍然不可替代。

概述

        Handler,Looper,MessageQueue,ThreadLocaL四個元件共同實作了android的線程間通信。為了友善了解整個通信機制,我們可以打個比方:

        Child Thread是一個淘寶網店,它要把一個快遞Message寄給買家Handler,Handler住在一個小區,小區叫UI Thread。快遞公司根據位址确定了你所在地的蜂鳥倉庫MessageQueue,并把快遞存放進去。快遞存在倉庫中了,這時候,承包UI Thread小區快遞生意的快遞小哥Looper就會更新一下Handler淘寶上的快遞資訊,說你的快遞已經到了XX倉庫,麻煩來拿一下。然後Handler就拿到了自己想要的Message。

Android Handler線程通信機制詳解及手動實作閑扯概述HandlerMessage與MessageQueueMessageQueueLooper總結自己實作一個Handler

        首先是一個小區不會限制網購物品的人隻能有一個,是以一個線程也不會限制Handler的數量;其次,小區Thread才不會分自己是買家小區還是賣家小區,買家和賣家都可以住在同一個小區,是以線程也不會以發送消息的線程,接受消息的線程去區分,所有線程裡都可以有Handler,但有條件;然後,這個條件也是相同的,為了讓買家能夠收到快遞,必須要有快遞小哥的幫助,如果沒有快遞小哥,說明該小區不在我們的服務範圍,不送的,既然收不到快遞,那這個小區肯定沒有買家,是以,一個線程裡能能不能有Handler取決于這個線程有沒有Looper執行個體。最後,一個小區一個快遞小哥就夠,多了搶生意,也不友善買家Handler去取件,而且小哥隻有能力去管一個倉庫MessageQueue,否則就忙不過來了,就是說每個Thread隻能最多一個Looper,一個Looper必負責一個MessageQueue。

        雖然在細節上還有點細微差別(比如下單的這個操作,大多數情況下不會涉及線程通信,因為相對于子線程,或者說是消息發送者,主線程,或說是消息接收者是其上帝,造物主。其一生的命運無法被改變,那就是發消息,發完,就被kill在這個例子中就是說賣家小區是買家小區建的,建的時候就已經指令賣家隻能幹什麼了,相當于工廠?);偶爾出問題,罷工,上帝沒拿到他想拿的東西,很生氣,于是kill了它,重新建立了個線程)。

        接下來我們通過源碼來認識一下真正的Handler機制。

Handler

        在說Handler的具體内容前,我們先想想它的作用:一是提供給消息發送者,告訴他消息傳遞的目标(對應于Message類中的target成員變量);二是通過一個接口成員變量mCallback的dispatchMessage方法來回調擷取消息。這些功能由那些函數來實作呢?我們翻翻源碼,發現了一大堆sendXXMessageXXX()和post(XXX xxx)方法用來發送資訊,我們稍微理一理調用關系就可以發現,實際上所有的消息發送,都歸到了一個方法身上

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

這十分容易了解:我不管你這個消息要幹嘛,有什麼要求,我的最終目的都是要把消息送出去,消息要送出去,那就傳給指定的消息隊列就好了啊,不管什麼sendXXMessageXXX()還是post(XXX xxx)無非也就是在傳遞消息前對消息進行了一些加工,或者是我這個消息其實是個Runnable,但它本質還是消息,并不能阻止消息會被加入消息隊列的真實。

        其次是接受消息,同樣如同發送消息,但有細微差別,所有的方法最後都調用了

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

你看到,欸,這不全最後全是handleMessage嗎,回歸根源後不是handleMessage才想到與enqueueMessage的地位嗎?我把擷取消息的根源給dispatchMessage不是因為它到頭了,而是因為到這裡(倒數第二步)分叉了。表面看上去都是handleMessage,但稍稍認真看一下就可以發現,其中一個是接口的回調方法,一個是直接調用的函數,大哥是你自己繼承Handler.Callback的接口函數,二哥是Handler.Callback自帶的接口函數,有大哥的時候大哥頂住,沒大哥二哥頂,大哥二哥都沒有,那找臭魚爛蝦沒接口的handleMessage也得頂住

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}
           

你看官方文檔也說的很明白了,沒有大哥二哥無所謂,但連臭魚爛蝦都沒有就根本頂不住。

        Handler的功能講的差不多了,我們再回過頭看看構造方法。首先,我們知道發送消息必須要MessageQueue,接受消息要通過Looper,Looper的内部包含了這個MessageQueue,那我們構造Handler的時候,一個Looper是必要的,但實際上我們很多時候使用Handler,并未去指定Looper,那是因為不指定Looper時,Handler會自動擷取目前線程Looper,UI線程自帶Looper,是以不用傳;其次,在接受消息的時候想自己定制一個大哥來幫我做事,好,那就自己寫個Callback傳給Handler,覺得二哥或者臭魚爛蝦夠用了,那就不需要這個Callback,是以這個Callback是可選的;最後,是一個關于Message的布爾變量,由于設定了預設值是以一樣可有可無(這涉及MessageQueue的入隊機制,在此先不多說,我們在MessageQueue部分來詳細介紹)。

        那麼構造方法按理接應該有2^3=8種,實際也差不多是這樣,一種以Looper和布爾變量為參的構造函數變了一下型,問題不大。

@NonNull
public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null, true);
}
           

Message與MessageQueue

Message

        單個Message執行個體很簡單,容器而已,沒什麼好說的,它的複雜性展現在許多Message回收機制及MessageQueue。唯獨注意一下

/**
*While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.
* /
public Message() {}
public static Message obtain() {...}
           

        這樣的好處是每次不需要新建立一個Message執行個體,而去消息池中拿一個不用的來代替,這樣可以減少申請記憶體的開銷。另外還有一組整型成員變量及其方法

/** If set message is asynchronous */
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;//左移一位2

/*package*/ int flags;

public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;//位或,flags值為
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;//位并
    }
}
           

這說明Messae不僅僅是隻有同步的(就是說,接收消息的線程接受不到消息,那我就一直堵着,消息來了在開工),也可以異步(先自己做點其他事,消息來了,再回頭做這件事)。關于Handler機制的文章很多,但關于這方面都基本沒有涉及,我會在MessageQueue中講解這部分(因為這涉及Handler的第三個構造參數)。

        為了補充上面的内容,現在先來說說MessageQueue。先是一些基本的概念:首先MessageQueue是一個連結清單,一個連結清單就注定有節點,在MessageQueue中節點就是一個一個的Message,單個的Message已經構成節點,沒有在建立一個節點類;

public class Message{
    ...
    public Message next;
}
           

其次,上文提到了,我們在執行個體化Message時,一般不會去new一個,而是從消息池中拿一個不用的——那麼消息池怎麼來的呢?我們先看看obtain的源碼

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

同步鎖的這段代碼,明顯的,我們可以發現,消息池中,消息仍以連結清單形式存在,而且頭删法擷取元素。在消息池不為空時,我們從消息池中拿,當消息池為空時,我們直接構造一個新的,但是,這個消息池不是天上掉下來的吧,我們需要去找往消息池中添加消息的方法——recycleUnchecked()

void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        /**
        *為什麼要把放在消息池裡的消息标記為正在使用?是因為改方法被recycle()方法封裝,recycle()中
        *對該值進行了檢驗,IN_USE的Message不會被回收,這樣保證了Message不會被重複回收。(被标記為IN_USE的
        *Message可能在消息池中,可能在MessageQueue中,也可能馬上回加入MessageQueue。)
        */
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }//頭插法向消息池中不停的添加消息
    }
}
           

recycleUnchecked()方法會在Message使用結束後調用。整個Message建立過程就是在擠牙膏,缺多少就擠多少,能夠極大的節約空間。

Message的種類

        在消息隊列中,我們把消息分為同步消息,異步消息和消息屏障。同步異步消息通過flags來進行區分,下面簡單說說他們的差別。首先在沒有消息屏障時,這兩種消息在消息隊列中沒有差別,當消息屏障出現時差別得以展現。簡單的來說,消息屏障就是一個target屬性為null的消息,當消息屏障處于消息隊列的隊首時,目前消息隊列就不再接收同步消息,且消息隊列中已有的所有同步消息均不執行,當有異步消息enqueue時,可以優先于隊列中所有的同步消息執行。

MessageQueue

        然後我們在來看MessageQueue,MessageQueue的許多方法實作在native層,對于native層的具體邏輯,對于整個Handler機制,沒有必要去深究,我們隻需要知道native層幫助我們完成了什麼功能就OK。首先,native層幫我們完成了對MessageQueue的執行個體化,MessageQueue調用nativeInit()方法,該方法放回一個long和MessageQueue指針。

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
           

且由于MessageQueue是在native層構造的,為了避免native層的調用,導緻我們無法回收該部分記憶體以緻OOM,native層提供了nativeDestroy()方法

@Override
protected void finalize() throws Throwable {
    try {
        dispose();
    } finally {
        super.finalize();
    }
}
           

        MessageQueue中消息發送者想消息隊列中發送消息,接受者從隊列中取出消息,其本質也是一個生産者-消費者模型,是以必然會存在阻塞與喚醒,但由于上文提到的消息屏障的問題,我們同時需要考慮消息屏障對于阻塞的影響。enqueueMessage()和next()源碼如下(分析見注釋)

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
        //不能通過enqueueMessage的方式來添加消息屏障,若要手動添加消息屏障,需反射MessageQueue,調用postSyncBarrier(Long when)方法
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
        //在消息隊列或消息池中的消息,不能入隊
    }

    synchronized (this) {
        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();
        msg.when = when;
        Message p = mMessages;//mMessage是整個隊列的頭結點
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {//三個判斷條件分别代表:1.隊列現在為空;2.該消息的等待時間為零;3.該消息的等待時間小于頭結點消息等待時間,這時我們把新消息作為隊列的頭結點
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);//用來叫醒接收線程
        }
    }
    return true;
}

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

通過enqueueMessage()和next(),我們可以更加清楚的認識同步,異步消息與消息屏障的入隊機制。

IdleHandler

        MessageQueue裡的一個接口,和整個消息隊列沒有什麼太大關系,簡單介紹一下(我也不是很會用(-_-))。IdleHandler主要作用是讓Handler在空閑的時候(目前MessageQueue中沒有消息等待處理),有點事情可以做(回調函數,通過設定其傳回值T或F來設定其一直執行或是執行一次後退出)。

Looper

      首先分析Looper接收消息的一些特性:

                1.Looper需要通路消息隊列來擷取消息;

                2.消息的數量與MessageQueue沒有關系,隻有消息發送者才知道自己要發多少條消息,Looper與發送者構成消費者-生産者的關系;

                3.消息的發送可能有延遲;

      在考慮我們對Looper的要求,保證每一條消息都可以準确的接收,那麼Looper的設計就必須滿足以下功能:

                1.能夠通路消息隊列,更加具體的來說是可以調用MessageQueue.next();

                2.由于需要不斷讀消息,且不知道消息的具體數量,同時需要根據生産隊列的具體情況阻塞自己,喚醒生産者;反之,當消息隊列中存在消息時,也同時可以喚醒Looper。

      為了滿足這樣的功能,首先我們需要寫一個循環,去不停的去消息隊列的資料,可是不知道具體要去多少個消息後該結束,不過我們知道如果沒有消息可取,那應該等生産者生産消息;同時,當生産者發出消息時,Looper要被喚醒,繼續循環。那麼實際Looper的工作狀态就是,循環——阻塞——循環——阻塞······,那麼答案就很明顯了,Looper就是個一直循環的東西。這樣一通分析,其實Looper的loop()方法大概我們也分析完了,同時,為什麼Looper是個死循環,卻并不會有問題的原因也解釋了。(說的書面一點是Looper在循環中由于阻塞會讓出自己的CPU時間)

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

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

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (slowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    slowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    slowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

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

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}
           

      但是還有問題,是關于Looper如何保證接收到的消息準确的。這就要從Looper的構造方法說起

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

首先構造方法私有,隻能通過prepare來執行個體化;其次,MessageQueue由Looper建立;最後,一個線程隻能有一個Looper。ThreadLocal在Looper中的主要目的是保證Looper單線程單例(若采用單例模式就需要加鎖,這個太影響效率了,單例加鎖相當于時間換空間,ThreadLocal相當于空間換時間)(為什麼設計Looper時需要單線程單例?自己實作Looper的時候不加ThreadLocal,多建立幾個Looper試試看),至于其本身的一些機制,這裡不細講。

總結

      就提一下一些重點吧:

            1.隻要是線程,都可以建Looper;隻要有Looper的線程,都可以用Handler;

            2.一個線程,一個Looper,一個MessageQueue,n個Handler;

            3.通過設定消息屏障,優先異步消息;

            4.native層,MessageQueue通過epoll多路I/O複用來進行線程通信。

自己實作一個Handler

      通過一定的了解,我們可以自己實作一個簡單的handler線程通信(也就單單模拟了一下通信功能,單個元件的功能并不完善),具體代碼如下

Handler

public class Handler {
    private final Looper looper;
    private MessageQueue mQueue;

    public Looper getLooper() {
        return this.looper;
    }    

    public Handler(Looper looper) {
        this.looper = looper;
        this.mQueue = looper.mQueue;
    }

    public void sendMesssage(Message msg, long time){
        MessageQueue queue = mQueue;
        enqueueMessage(queue,msg,time);
    }

    private void enqueueMessage(MessageQueue queue,Message message,long time){
        message.target = this;
        queue.enqueueMessage(message,time);
    }

    public void dispatchMessage(Message message){
        handleMessage(message);
    }

    private void handleMessage(Message message){
    }
}
           

Looper

public class Looper {
    private static ThreadLocal<Looper> threadLocal = new ThreadLocal<>();
    public MessageQueue mQueue;

    public Looper() {
        this.mQueue = new MessageQueue();
    }

    public static Looper myLooper(){
        return threadLocal.get();
    }

    public static void prepare(){
        Looper looper = myLooper();
        if (looper == null) {
            threadLocal.set(new Looper());
        } else {
            throw new RuntimeException("Looper has been prepared in the current thread.");
        }
    }

    public static void loop(){
        Looper looper = threadLocal.get();
        if (looper == null) {
            throw new RuntimeException("Have you call the method prepare() before?");
        }
        MessageQueue messageQueue = looper.mQueue;
        while (true){
            Message msg = messageQueue.next();
            if (msg == null) {
                continue;
            } else {
                msg.target.dispatchMessage(msg);
            }
        }
    }
}
           

Message

public class Message {
    public int what;
    public Object obj;
    public Handler target;
    public long time;
}
           

MessageQueue

public class MessageQueue {

    Lock lock;
    Condition targetThread;
    Condition messageProduceThreads;
    private int MAX_INDEX = 10;//調整隊列最大長度
    private LinkedList<Message> list = new LinkedList<>();
    /**
    *選用LinkedList來表示隊列,隻是因為懶,不想去重新寫,兩者差距還是很大的
    *且LinkedList不支援insert,是以時間參數雖然寫了,但未展現出異步消息的特點
    */
   public MessageQueue() {
        lock = new ReentrantLock();
        targetThread = lock.newCondition();
        messageProduceThreads = lock.newCondition();
    }

    public  Message next() {
        Message message = new Message();
        try{
            lock.lock();
            while(list.size() == 0) {
               targetThread.await();
            }
            message = list.getFirst();
            list.removeFirst();
            messageProduceThreads.signalAll();
        } catch (InterruptedException e){
                e.printStackTrace();
        } finally {
            lock.unlock();
            return message;
        }
    }

    public void enqueueMessage(Message message, long time) {
        message.time = time;
        try {
            lock.lock();
            while (list.size() == MAX_INDEX){
                messageProduceThreads.await();
            }
            list.add(message);
            targetThread.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
           

使用

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            Handler handler = new Handler(Looper.myLooper()){
                @Override
                public void dispatchMessage(Message message) {
                    System.out.println("handler" + "我get到了"+ String.valueOf(message.what) + "---" + String.valueOf(message.obj));
                }
            };
            //自己測試時對線程數量進行随意調整
            for (int i = 0;i<30;i++){
                final int num = i;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = new Message();
                        msg.what = num;
                        msg.obj = Thread.currentThread().getId();
                        handler.sendMesssage(msg,System.currentTimeMillis());
                    }
                }).start();
            }
            Looper.loop();
        }
    }).start();
}