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。
首先是一個小區不會限制網購物品的人隻能有一個,是以一個線程也不會限制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();
}