post代码调用链:
调用post
时候会先调用getPostMessage
生成一个Message
,后面和sendMessage
的流程一样。下面看下getPostMessage
方法的源码:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到getPostMessage
中会先生成一个Messgae
,并且把runnable
赋值给message
的callback.
消息都放到MessageQueue
中后,看下Looper
是如何处理的。
for (;😉 {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
msg.target.dispatchMessage(msg);
}
Looper
中会遍历message
列表,当message
不为null
时调用msg.target.dispatchMessage(msg)
方法。看下message
结构:
也就是说msg.target.dispatchMessage
方法其实就是调用的Handler中的dispatchMessage
方法,下面看下dispatchMessage
方法的源码:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//
private static void handleCallback(Message message) {
message.callback.run();
}
因为调用post
方法时生成的message.callback=runnable
,所以dispatchMessage
方法中会直接调用 message.callback.run();
也就是说直接执行post
中的runnable
方法。 而sendMessage
中如果mCallback
不为null
就会调用mCallback.handleMessage(msg)
方法,否则会直接调用handleMessage
方法。
总结 post
方法和handleMessage
方法的不同在于,post
的runnable
会直接在callback
中调用run
方法执行,而sendMessage
方法要用户主动重写mCallback
或者handleMessage
方法来处理。
首先给出结论,Looper
不会一直消耗系统资源,当Looper
的MessageQueue
中没有消息时,或者定时消息没到执行时间时,当前持有Looper
的线程就会进入阻塞状态。
下面看下looper
所在的线程是如何进入阻塞状态的。looper
阻塞肯定跟消息出队有关,因此看下消息出队的代码。
消息出队
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 nextPollTimeoutMillis = 0;
for (;😉 {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
if(hasNoMessage)
{
nextPollTimeoutMillis =-1;
}
}
}
上面的消息出队方法被简写了,主要看下面这段,没有消息的时候nextPollTimeoutMillis=-1
;
if(hasNoMessage)
{
nextPollTimeoutMillis =-1;
}
看for循环里面这个字段所其的作用:
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
Binder.flushPendingCommands();
这个方法的作用可以看源码里面给出的解释:
/**
-
Flush any Binder commands pending in the current thread to the kernel
-
driver. This can be
-
useful to call before performing an operation that may block for a long
-
time, to ensure that any pending object references have been released
-
in order to prevent the process from holding on to objects longer than
-
it needs to.
*/
也就是说在用户线程要进入阻塞之前跟内核线程发送消息,防止用户线程长时间的持有某个对象。再看看下面这个方法:nativePollOnce(ptr, nextPollTimeoutMillis);
当nextPollingTimeOutMillis=-1
时,这个native
方法会阻塞当前线程,线程阻塞后,等下次有消息入队才会重新进入可运行状态,所以Looper
并不会一直死循环消耗运行内存,对队列中的颜色消息还没到时间时也会阻塞当前线程,但是会有一个阻塞时间也就是nextPollingTimeOutMillis>0
的时间。
当消息队列中没有消息的时候looper肯定是被消息入队唤醒的。
消息入队
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException(“Message must have a target.”);
}
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;
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;
}
上面可以看到消息入队之后会有一个
if (needWake) {
nativeWake(mPtr);
}
方法,调用这个方法就可以唤醒线程了。另外消息入队的时候是根据消息的delay
时间来在链表中排序的,delay
时间长的排在后面,时间短的排在前面。如果时间相同那么按插入时间先后来排,插入时间早的在前面,插入时间晚的在后面。
4、android的Handle机制,Looper关系,主线程的Handler是怎么判断收到的消息是哪个Handler传来的?
Looper
是如何判断Message
是从哪个handler
传来的呢?其实很简单,在1
中分析过,handler
在sendMessage
的时候会构建一个Message
对象,并且把自己放在Message
的target
里面,这样的话Looper
就可以根据Message
中的target
来判断当前的消息是哪个handler
传来的。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
学习分享
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
七大模块学习资料:如NDK模块开发、Android框架体系架构…
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
oid框架体系架构…**
[外链图片转存中…(img-ykCkKXPQ-1712624235472)]
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
第三,到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。如果你有需要,我这里恰好有为什么,不来领取!说不定能改变你现在的状态呢!
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-YfQDNkJG-1712624235472)]