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