handler相关的blog不计其数。不好意思,没有记住,就是没有了解到,无法和已有的知识进行相关,嗯,知识薄弱,相关不上来。大家都知道,要想小孩子聪明,得多吃鱼,吃鱼油啥的,成年人想要聪明,一般是选用吃亏的方法。
handler相关的blog,真的看了许多遍,知识不进脑子,并且源码,也点了好几遍,感觉就没有感觉,就是那种单相思的感觉,我喜欢她她不喜欢我,然后我另寻新欢,但是架不住从头喜欢,循环往复。但是从本年初步,啥岗位都问handler,外包也问,问多了,结合面试官的提点,然后从头学习下,忽然就感觉可以入门了的感觉。
正文
handler的运用
要想了解一个东西,最好的方法应该是通过了解他处理的痛点是啥,然后去考虑他怎样处理这个痛点。
主线程中创建一个handler
class MyHandler:Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message) {
LogUtils.e(msg.obj)
}
}
创建方针:
val myHandler by lazy {
MyHandler()
}
发送消息:
myHandler.sendMessage(Message.obtain().apply {
obj=System.currentTimeMillis()
})
清空消息:
override fun onDestroy() {
super.onDestroy()
myHandler.removeCallbacksAndMessages(null)
}
子线程中创建一个handler
class LooperThread : Thread() {
lateinit var mHandler: Handler
override fun run() {
Looper.prepare()
mHandler = object : Handler(Looper.myLooper()!!) {
override fun handleMessage(msg: Message) {
LogUtils.e(msg.obj)
}
}
Looper.loop()
LogUtils.e("循环退出")
}
}
那么我们需求运转子线程:
lateinit var localThread: LooperThread
localThread=LooperThread().apply {
start()
}
发送消息:我们这儿发送一个时间戳
localThread.mHandler.sendMessage(Message.obtain().apply {
obj=System.currentTimeMillis()
})
关闭handler:
localThread.mHandler.looper.quit()
我们知道。Looper.loop() 可以了解成一个死循环,所以说,我们自定义线程中的handler 需求关闭looper的循环。那么我们在主线程中的handler 可以关闭吗?
答案是不可的,通过主线程的代码,我们可以看到,我们运用的是主线程looper,所以关闭掉,APP就直接死掉了。然后会抛一个失常: java.lang.IllegalStateException: Main thread not allowed to quit. 奉告你,主线程不能这么搞。
好像,看不出来他处理了什么,感觉就是一个通讯东西。
那么Android主线程是怎样做的
主线程looper 的创建以及ThreadLocal
在ActivityThread类中的main 函数中首要调用了:
Looper.prepareMainLooper();
细究代码就可以发下,我们上面创建的Looper 方针的quitAllowed是true,而体系层的确实false.
- 当quitAllowed为TRUE的时分,用于创建一个可以被退出的Looper。
- 当quitAllowed 为false的时分,用于创建一个不可被退出去的Looper 方针。
而通过looper的结构函数可以看到:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
looper 中创建了一个MessageQueue,而quitAllowed 则是给了MessageQueue,然后获取了下其时线程。在函数prepare中:
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));
}
sThreadLocal则是ThreadLocal 的实例。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
我们将其时的Looper 方针创建成功后,便放到ThreadLocal 中。
ThreadLocal 它为每个线程供给了一个独立的变量副本。这意味着每个线程都可以设置其自己的ThreadLocal变量,而不会影响其他线程的变量。
ThreadLocal常用于完结线程局部变量。这种变量在每个线程中都有自己的副本,并且只对其时线程可见,而不会影响其他线程。这关于开发多线程运用程序十分有用,因为它可以防止多个线程之间共享变量时或许出现的问题,例如数据不一同性、竞争条件等。
ThreadLocal类的首要方法是get()和set()。get()方法用于获取其时线程的ThreadLocal变量值,而set()方法用于设置其时线程的ThreadLocal变量值。
结合上面这句话,我们就可以知道以下几个面试题的答案了:
- 为什么主线程中不调用prepare,因为ActivityThread 现已调用了,再次调用会报错。
- 为什么一个线程只能调用一次prepare,因为这个代码就是这么写的,创建一次后,再次创建会报错。
- looper方针怎样怎样存储的,ThreadLocal。
loop 从死循环到怎样发送消息进行相关
其实,代码都在这儿,不论他怎样问,都是不会脱离ThreadLocal,这才是中心。我们继续往后看,在ActivityThred 的main 函数中:
Looper.loop();
这是不是和我们自己在子线程中调用是一同的,但是,到这儿,我们好像没有看到handler 绑定looper 的进程,因为我们在上面的代码里面创建handler的时分,都传递了一个looper,那么是否说明,有一种计划,是可以不传递looper的,我们通过Handler的结构函数可以发现:
class MyHandler2:Handler(){
override fun handleMessage(msg: Message) {
LogUtils.e(msg.obj)
}
}
这么写也可以接受到handler 消息。而在ActivityThread中:
final H mH = new H();
这个调调就是主线程中的handler。这个扯远了,我们仍是来看Looper.loop()的完结,其他的先不论可以看到这个里面有一段这样的代码:
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
这个me,贼要害,就是looper 本身。这个就是我们常说的死循环的完结。在loopOnce函数中:
Message msg = me.mQueue.next(); // might block
所以。我们回想下,我们创建looper 的时分,是不是自己创建了一个方针,这个方针就是mQueue,所以,这个死循环就一贯检验通过mQueue 进行消息获取。
所以,我们这儿代码就走到了,mQueue.next() 的回来值是一message,所以我们来看下怎样发送消息。
myHandler.sendMessage(Message.obtain().apply {
obj=System.currentTimeMillis()
})
-
调用handler.sendMessage()
-
sendMessage() 调用:sendMessageDelayed(msg, 0)
-
sendMessageDelayed(msg, 0) 调用:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) ,这个玩意贼要害,因为有些面试官要考这个玩意。在这儿,我们将其时时间和延时时间相加,得到一个handler实行的预期时间。
-
enqueueMessage(queue, msg, uptimeMillis) 调用到了这儿,可以看到,新增了一个入参,这个queue就是我们handler 结构函数传入的looper中的mqueue,但是我们记住handler 可以不传递looper的:
public Handler(@Nullable Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
通过handler的结构函数,可以看到,假设走不传递looper 的结构函数,那么他会通Looper.myLooper() 获取到一个,结合上面的知识点,我们looper 方针存储到ThreadLocal中,所以每个线程中获取到的方针都不相同。所以在子线程没有调用:Looper.prepare()那么就会报错。我们处理了queue怎样来的问题后。再来看handler 怎样发送一条消息
-
在函数:enqueueMessage中:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
- 可以看到,他对msg的target进行了赋值,一同将mAsynchronous 传递给了msg,这个就尤为的重要的。mAsynchronous用于符号是否是异步使命。
- 终究,将消息存储到了mQueue 中。
结合上面的循环通过mQueue 获取消息就知道,handler 其实是一个中心方针,担任发送消息和消费消息。循环通过looper 办理,mQueue 则才是消息的存储者和决议其时什么消息被消费的办理者。
怎样存储消息
我们先来看queue.enqueueMessage(msg, uptimeMillis)。这个函数的完结:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 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;
}
- 首要判别了target 是否等于空,结合上面的信息,我们在enqueueMessage 设置了target,已然判别了,说明有当地答应msgtarget 为空。
- 第二步,判别message 是否被运用,一般来说,不会出现这种,情况,因为我们运用message 是通过Message.obtain() 完结的,但是架不住自己创建一个message方针,屡次通过handler 发送。Message.obtain() 就涉及到享元方式了(这个后边写)
- 判别mQuitting,这个是我们撤销子线程循环的时分调用mHandler.looper.quit() 的时分赋值为true。也就是说,被中止了,handler 也无法增加消息了。
- 然后设置messae 被运用情况,也就是调用:msg.markInUse()
- if (p == null || when == 0 || when < p.when) 这个条件,因为mMessages 默许是空,所认为空的时分是第一次,when=0,我们发送消息的时分,when 是给了时间戳的,说明有其他当地发送when=0的message, when < p.when 也是相同的情况,一般来说synchronized 结合我们时间戳,发送延时消息的时分,就会出现头一个节点消息是延时小时,后一个消息不是延时消息,就会将后一个消息放到前面。通过翻译代码注释:New head, wake up the event queue if blocked. 通过这个代码可以看到,他重置了链表的头节点,后续的message 都将增加到这个新的链表里面去。
- 后续的代码就是将新增加到message 增加到链表的终究一个节点或许基于when < p.when,将新增加到message 增加到P的上一个节点。那么什么时分增加到小于上一个呢? 那就是发送延时消息的时分,比如B是延时消息5毫秒,先send B 消息,A则是马上发送的消息,send A 的时分,就会把A放到B的前面。
到这儿,我们通过自定义handler 怎样增加消息就现已根本搞清楚了。但是还有一个问题他用的是SystemClock.uptimeMillis():
SystemClock.uptimeMillis()
是一个Java方法,它回来自体系发动以来通过的毫秒数。这并不包括睡觉时间或暂停时间。这个方法常被用于计算程序的运转时间或许在一段时间内实行某些操作。在Android中,SystemClock.uptimeMillis()只要在体系重启后才会从头从0初步。该方法回来的是自体系发动以来通过的毫秒数,因此假设设备没有重启,那么uptimeMillis()将一贯增加。假设设备从头发动,那么uptimeMillis()将从头从0初步计数。请留心,假设设备进入休眠情况或屏幕关闭等低功耗方式,uptimeMillis()仍会继续计数。这是因为uptimeMillis()是体系发动后的时间,不受设备是否在运用或处于休眠情况的影响。
所以说,这个玩意可以做埋点快速跳转的指纹的参数,这或许是手机一段时间不要害就感觉有点卡的其间一片雪花吧。
到这儿,我们其实就可以知道,mQueue 其实是通过时间戳结合链表对消息队伍进行排序的,而为什么可以发送延时消息,也是因为在刺进数据的时分就现已处理了。
怎样决议应该消费什么消息
OK,我们发送消息大约看完了,我们再次回到mQueue.next() 上来,结合上面的信息,我们looper.loop 死循环里面在一贯调用这个这个函数。可以在函数中存在这么的代码块:
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());
}
这是什么呢? 还记住我们handler 的结构函数吗?结构函数中mAsynchronous 用于符号其时handler 发送的是否是异步消息。而在enqueueMessage函数中,关于message 方针设置中除了赋值了我们的handler 自己,还设置了mAsynchronous。所以从这儿可以看出handler 存在两种消息:
- 同步消息
- 异步消息
我们知道 mMessages 是链表的头节点,那么,而traget自定义handler 的里面是百分百赋值的,那么msg != null && msg.target == null 判别和msg != null && !msg.isAsynchronous() 这两个条件什么时分触发呢?
这就涉及到一个知识点,我们自定义view和改写view的时分,调用了invalidate和requestLayout,而这两个函数都调用了:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
要害在于调用了 mHandler.getLooper().getQueue().postSyncBarrier(),而在postSyncBarrier函数的完结中:
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
这儿可以看到,这个msg 是没有设置target的。所以只需链表头是通过 postSyncBarrier() 发送的消息,那么就必定满足:msg != null && msg.target == null 这个条件。在postSyncBarrer 函数中剩下的代码:
Message prev = null;
Message p = mMessages;
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;
}
在if(when!=0) 判别中,首要是通过链表头查找链表链表中when 大于的这个节点,当大于了whlie 就退出循环了。需求把这个消息刺进到这个节点的后边。假设大于了,说明是推延消息正常处理即可。假设没有就讲这个消息设置为头节点。因为我们这个链表是通过when 进行排序的,小的在前面。
我们在来看下面的代码:
static final int FLAG_ASYNCHRONOUS = 1 << 1;
static int flags;
public static boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
isAsynchronous() 这个函数代码必定回来false,所以msg != null && !msg.isAsynchronous()这个条件也是有满足的条件的,只需通过postSyncBarrier 发送消息即可。而发送postSyncBarrier() 通过注释翻译过来就是设置一个同步屏障:
postSyncBarrier 方法的首要作用是在消息队伍中设置一个同步屏障。当消息队伍遇到这个屏障时,会暂停后续消息的处理,直到调用了 removeSyncBarrier 方法并传入相应的 token 来移除这个屏障。这个方法常常用于完结一些需求等候特定条件满足才干继续实行的消息处理逻辑。
具体来说,假设你需求在某些消息处理完之后,再处理其他消息,就可以运用 postSyncBarrier 和 removeSyncBarrier 来完结。例如,你或许需求先处理一些初始化消息,然后再处理用户输入的消息。在这种情况下,你可以在处理完初始化消息后,调用 postSyncBarrier 方法设置一个屏障,然后继续处理用户输入的消息。当全部的用户输入消息处理完后,再调用 removeSyncBarrier 方法移除屏障,这样后续的消息就可以继续处理了。
需求留心的是,removeSyncBarrier 有必要与 postSyncBarrier 配对运用,否则会导致消息队伍无法正常作业,程序或许会挂起。
所以上面代码:
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
的退出条件是:找到一个异步消息或许队伍为空了,假设链表头是一个同步屏障标志(没有target)这个do{}while 会去查找一个异步消息直到链表遍历到终究一个节点。当退出循环后:
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;
}
}
这个函数代码分为两种情况:
- 当头节点是消息屏障消息的时分。结合最初步msg = msg.next 代码,我们可以一个作业,就是假设链表中没有找到异步消息,那么msg 就是空,假设找到了,msg 就是找到的第一个异步消息。剩下的就是逻辑判别了,假设其时时间不小于异步消息的时间when,那么就讲这个异步消息发送出去。当然还处理头节点相关的联络,如 prevMsg.next = msg.next;这种情况下,就会发现,假设不移除消息屏障,头节点永远不变,那么后续的同步消息就一贯接收不到。即便是我们回来了异步消息,然后下次循环进来,仍是通过消息屏障设置的头节点在找异步消息。
- 还有一种情况是头节点不是消息屏障消息,那么也是将头节点发送出去,并且将next节点设置成头节点。
。上面代码没有贴完。我们在看无缺的代码:
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;
}
}
可以看到,这个外层包裹了一层 for (;;) {} 这种死循环,退出的时分,也就只要msg 不为空的时分才退出函数。当然上面的ptr == 0和mQuitting也是支撑回来null 退出函数的。mQuitting是需求我们调用quit()才会置为true,到这儿,这个函数中获取message的进程大致都整完了,当然还剩下一些:
- mPtr 怎样赋值,作用。当实行dispose(); mPtr 就会赋值为0。所以函数前面回来null 的逻辑也找到了,当然还有一些其他的关于mPtr 的细节。
- pendingIdleHandlerCount 的作用。
- Binder.flushPendingCommands();
- nativePollOnce(ptr, nextPollTimeoutMillis);
- 后边剩下的就是mIdleHandlers 相关的处理了,这个感觉的独自写,现在只需求记住 在next 里面实行了 keep = idler.queueIdle(); 并且移除了他即可。实行条件是什么呢?我们上面没有找到msg,并且头节点为Null或其时时间小于头节点的时分,然后就初步实行mIdleHandlers。就是说,循环里面不处理消息的时分,就是闲暇情况的时分,就处理这个。
怎样消费一个消息
上面我们看到了,怎样获取到一个msg,然后有消息屏障,异步消息,同步消息,知道了头节点归于消息屏障的时分,会实行找到一个最近的异步消息,假设没有找到就一贯循环找,直到消息屏障被移除,当没有消息的时分,会实行idlerHandler。仍是回到loopOnce 函数,我们这儿首要是获取一个msg 然后实行。我们知道next 函数可以回来空的,那是在调用quit() 函数后,所以:
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
这儿的判别结合looper.loop里面的:
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
就完结了死循环的退出(这玩意,好像也有人问)。继续函loopOnce函数里面的:
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
通过Looper里面获取到了一个logging,简略的检测其实就可以通过这么做,网络上许多类型的blog就是增加logging 完结的。而实行的当地就是在loopOnce函数里面。在这个函数里面有这么要害点一句话:
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
我们知道有消息屏障,但是真实回来的消息必定是有target的,所以在这儿,就调用了dispatchMessage:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
所以说,不论是callback,仍是重写handleMessage 也是在这儿调用的。
message的享元方式
我们运用message 的时分,往往都是通过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();
}
我们知道Message 其实也是一个链表,这儿的逻辑也很简略,就是判别链表头存不住,假设不存在就直接new,假设存在就回来链表头,将链表下一个设置为新的链表头,链表长度-1,然后设置flags 之类的。
那么什么时分增加进去呢?必定是消费后,我们从上面的代码可以看到,增加到消息的时分对消息是否被消费的flags 进行了判别。在loopOnce 里面终究几行有这么一句代码:
msg.recycleUnchecked();
recycleUnchecked的完结:
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
可以看到在synchronized里面,就是将收回的增加到链表的头部。一同判别了下长度。当然了message 还有一些其他的操作,比如不同情况下的flags等。
怎样移除消息屏障
我们知道增加消息屏障是调用的是postSyncBarrier(),那么移除消息屏障呢?就是removeSyncBarrier(token),
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;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
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);
}
}
}
还记住 我们发送同步消息屏障创建msg 的代码:
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
他为msg 设置了一个arg1=token,一同target=Null, while (p != null && (p.target != null || p.arg1 != token)) 这个循环的退出条件也狠简略,链表循环完了或许找到一个同步消息屏障。再结合下面的代码:
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
怎样没有找到同步消息屏障,那么就直接抛失常了。剩下的逻辑就简略了:
- prev 不为空的情况下,说明同步消息屏障没有在链表头部,就直接越过这个屏障节点:prev.next = p.next
- 当prev为空的时分,说明消息屏障是头节点, mMessages = p.next,相同越过屏障节点即可。
MessageQueue native 相关函数
- nativeInit 用于初始化消息队伍,他创建了一个新的消息队伍,并预备用于接收和发送消息。结构函数中的:mPtr = nativeInit();
- nativeDetroy,该函数用于毁掉消息队伍,在不需求消息队伍的时分调用,用于开释相关资源。
- nativePollOnce 此函数用于从消息队伍中轮询消息,他等候直到有消息可用,或指定的超时时间以已过。我们next for 循环里面就有这行代码。
- nativelsPolling ,此函数用于检测其时线程是否正在轮序消息,假设是就是true
- nativeSetFileDescrptorEvents 用于设置文件描述符工作,是一种通讯机制,当文件描述符情况发送变化时,例如,数据可以读取或许写入,操作体系会告知运用程序。
- nativeWake,此函数用于唤醒正在轮序的消息线程,假设一个线程正在轮序消息,并处于休眠情况,则调用此函数当即唤醒该线程,使其继续轮询消息。
好像没有看到轮序暂停的相关native 函数,没有看到aosp 中的完结代码,这一块就是迷糊的。
pendingIdleHandlerCount
pendingIdleHandlerCount
是一个在Android的 Looper
类中运用的内部变量。它用于存储其时在工作队伍中挂起的 Handler
实例的数量。
当一个 Handler
实例调用 Looper.post()
或 Looper.postAtFrontOfQueue()
方法将消息增加到工作队伍时,假设该消息不能当即实行(例如,假设工作队伍为空或许正在进行其他处理),那么这个 Handler
实例就会挂起,直到工作队伍变得可用。在这个进程中,pendingIdleHandlerCount
就会增加。
当一个 Handler
实例从工作队伍中取出一个消息并初步处理它时,pendingIdleHandlerCount
就会削减。当 pendingIdleHandlerCount
变为0时,表示没有挂起的 Handler
实例,工作循环可以进入闲暇情况。
请留心,这是一个内部变量,一般不会在你的运用代码中直接运用。它首要用于 Android 体系内部的消息传递和工作处理机制。
Binder.flushPendingCommands();
Binder.flushPendingCommands()
是Android的API方法,它用于强制实行全部挂起的指令。这可以保证全部的方法调用都会当即实行,即便这些方法原本或许会推延实行。
在Android中,一些操作(例如,I/O操作,网络操作等)或许会被安排为异步实行,即这些操作不会当即实行,而是会在未来的某个时间点实行。在这种情况下,Binder.flushPendingCommands()
可以用来强制当即实行这些操作。
但是,需求留心的是,过度运用或不妥运用此方法或许会导致功用问题或意外的行为。例如,假设在主线程(UI线程)中频频地调用此方法,那么或许会打乱运用的正常工作循环,导致界面卡顿或闪耀。
在大多数情况下,你应该让Android体系自行办理异步操作。只要在极少数需求当即响运用户输入或防止潜在的推延问题的情况下,才应该运用此方法。
完毕
到这儿,我们根本上是将handler 怎样发送消息,怎样分发消息给整理解了,当然有图的话更容易了解,这个后期再补吧,首要是大佬们写的根本上都带图,感觉抄袭一个没有必要,要自己画才有意思。
当然了,还有许多细节上的东西,这只能是一次简略的汇总吧。