看完这篇,别再说你不了解Handler消息机制了腾讯云开发者社区

首先看看在Android中,是怎么使用Handler往子线程发送消息的

publicstaticMessageobtain(){synchronized(sPoolSync){if(sPool!=null){Messagem=sPool;sPool=m.next;m.next=null;m.flags=0;//clearin-useflagsPoolSize--;returnm;}}returnnewMessage();}这里对Message的复用做了同步处理,如果Message池不为空,将sPool指针后移一个,将原来的头结点m返回,同时计数减1。这是非常熟悉的单链表操作。如果没有可以复用的,那么就创建一个新的Message。其他的obtain方法的重载都会调用此方法,然后将传入参数重新赋值。

在没有设置同步屏障时,普通消息和异步消息没有不同,设置同步屏障之后,同步屏障之前的消息正常执行,同步屏障之后的所有同步消息不能执行,异步消息会优先执行;

同步屏障需要手动移除,同步屏障如果一直不移除,当所有异步消息执行完之后,线程会被挂起。

同步屏障一般是系统行为,我们无法手动调用,除非反射;Android中View的绘制任务就是通过发送同步屏障和异步消息的方式实现的。

nativePollOnce(ptr,nextPollTimeoutMillis);synchronized(this){//Trytoretrievethenextmessage.Returniffound.finallongnow=SystemClock.uptimeMillis();//指向前一个messageMessageprevMsg=null;//初始时指向第一个messageMessagemsg=mMessages;//1msg.target==null说明遇到消息屏障if(msg!=null&&msg.target==null){//能进入这个if,说明此时的msg是屏障消息//循环遍历,退出循环的条件是,message到末尾了,或者//msg是异步消息do{prevMsg=msg;msg=msg.next;}while(msg!=null&&!msg.isAsynchronous());}主线程的Thread是什么时候创建的实际上Android主线程(UI线程)并不是通过显式实例化Thread然后调用start()实现的。应用启动时,zygote进程fork出应用进程的时候,这个进程的入口点就是主线程,可以理解为每个进程都有一个默认线程,这个默认线程就是主线程。fork出应用进程之后,会调用ActivityThread.main()方法,此时main方法就是跑在主线程的,然后这里调用Looper.prepareMainLooper()以及Looper.loop()方法。所以这也是为什么在主线程中可以直接实例化Handler的原因。

//这个方法是专门为主线程用的,手动再调用一次会抛异常@DeprecatedpublicstaticvoidprepareMainLooper(){prepare(false);synchronized(Looper.class){if(sMainLooper!=null){thrownewIllegalStateException("ThemainLooperhasalreadybeenprepared.");}sMainLooper=myLooper();}}looper是怎么退出的从loop方法的实现中可以看到,当消息队列next返回空的时候,会退出循环。通过调用Looper实例的quit()或者quitSafely()可以实现将循环退出,方法会设置消息队列的退出标志,并添加一个空消息到消息队列中。再下一次执行next()方法的时候,决定是否要终止循环。同时主线程循环不可以通过调用quit或者quitSafely退出的,这是因为这两个方法最终会调用到MessageQueue的quit方法中,这里会判断如果mQuitAllowed为false的时候,会抛出异常。而mQuitAllowed是在创建MessageQueue的时候传入的,主线程的Looper.prepareMainLooper内调用prepare传参传入的是false,所以主线程不支持退出循环,当应用进程kill的时候循环停止。而我们自己创建的工作线程,我们调用的是Looper.prepare,内部传参是true,所以工作线程支持退出循环。

//给子线程调用的publicstaticvoidprepare(){prepare(true);}privatestaticvoidprepare(booleanquitAllowed){if(sThreadLocal.get()!=null){thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");}sThreadLocal.set(newLooper(quitAllowed));}//给主线程调用的publicstaticvoidprepareMainLooper(){prepare(false);synchronized(Looper.class){if(sMainLooper!=null){thrownewIllegalStateException("ThemainLooperhasalreadybeenprepared.");}sMainLooper=myLooper();}}quit和quitSafely区别和原理最终会调用到MessageQueue的quit方法

voidquit(booleansafe){if(!mQuitAllowed){thrownewIllegalStateException("Mainthreadnotallowedtoquit.");}synchronized(this){if(mQuitting){return;}mQuitting=true;if(safe){removeAllFutureMessagesLocked();}else{removeAllMessagesLocked();}//WecanassumemPtr!=0becausemQuittingwaspreviouslyfalse.nativeWake(mPtr);}}privatevoidremoveAllMessagesLocked(){Messagep=mMessages;while(p!=null){Messagen=p.next;p.recycleUnchecked();p=n;}mMessages=null;}privatevoidremoveAllFutureMessagesLocked(){finallongnow=SystemClock.uptimeMillis();Messagep=mMessages;if(p!=null){if(p.when>now){removeAllMessagesLocked();}else{Messagen;for(;;){n=p.next;if(n==null){return;}if(n.when>now){break;}p=n;}p.next=null;do{p=n;n=p.next;p.recycleUnchecked();}while(n!=null);}}}从源码上来看,quit()和quitSafely()都是在下一次消息出队的时候决定是否终止循环,正在处理中的消息不会被终止,区别就是

调用quit方法之后,MessageQueue中的mQuitAllowed会置为true,后续调用sendMessage都会返回false

booleanenqueueMessage(Messagemsg,longwhen){if(msg.target==null){thrownewIllegalArgumentException("Messagemusthaveatarget.");}synchronized(this){if(msg.isInUse()){thrownewIllegalStateException(msg+"Thismessageisalreadyinuse.");}if(mQuitting){IllegalStateExceptione=newIllegalStateException(msg.target+"sendingmessagetoaHandleronadeadthread");Log.w(TAG,e.getMessage(),e);msg.recycle();returnfalse;}//...}当消息队列中没有更多消息的时候,会发生什么?当前线程会阻塞在loop()里面的mQueue.next()方法中,MessageQueue的next()方法中也有一个死循环,没有更多消息的时候,会阻塞在这个循环里面。这里不是简单的死循环,所以阻塞不会消耗太大的资源,无消息的时候线程会进入休眠状态,CPU资源会被释放给其他线程使用;当有新消息的时候,线程会被唤醒,继续执行loop

queueIdle实现的时候返回true,则可以监听下一次空闲,如果返回false,这一次回到之后,MessageQueue就会把这个监听从列表中删除,下一次空闲就监听不到了。

publicbooleanpost(Runnableaction){finalAttachInfoattachInfo=mAttachInfo;if(attachInfo!=null){returnattachInfo.mHandler.post(action);}//Postponetherunnableuntilweknowonwhichthreaditneedstorun.//Assumethattherunnablewillbesuccessfullyplacedafterattach.getRunQueue().post(action);returntrue;}可以看到这里有两个分支,当attachInfo不为空的时候走attachInfo.mHandler.post(action);否则走getRunQueue().post(action)

通过对源码View搜索,可以知道attachInfo赋值的时机有两个:attachToWindow赋值;detachedFromWindow置为空

而dispatchAttachedToWindow()是从ViewRootImpl的performTraversals()中调过来的

前面已经解释过,attachInfo不为空的时候,代表View的measure、layout、draw正在执行,或者已经执行完了,此时通过attachInfo.mHandler.post(action)再往主线程post一个任务,必然能拿到宽高

当attachInfo为空的时候,执行的是getRunQueue().post(action),getRunQueue是什么呢?搜索View的源码,我们发现,这是一个任务队列,post(action)即往这个队列中加入一个任务,在ViewdispatchAttachedToWindow()的执行执行这个任务队列中的任务

voiddispatchAttachedToWindow(AttachInfoinfo,intvisibility){mAttachInfo=info;//...//Transferallpendingrunnables.if(mRunQueue!=null){mRunQueue.executeActions(info.mHandler);mRunQueue=null;}//...onAttachedToWindow();//...}executeActions就是把任务post到主线程处理

publicclassHandlerActionQueue{privateHandlerAction[]mActions;privateintmCount;publicvoidpost(Runnableaction){postDelayed(action,0);}publicvoidpostDelayed(Runnableaction,longdelayMillis){finalHandlerActionhandlerAction=newHandlerAction(action,delayMillis);synchronized(this){if(mActions==null){mActions=newHandlerAction[4];}mActions=GrowingArrayUtils.append(mActions,mCount,handlerAction);mCount++;}}//...publicvoidexecuteActions(Handlerhandler){synchronized(this){finalHandlerAction[]actions=mActions;for(inti=0,count=mCount;i

THE END
1.系统架构笔记2计算机系统基础知识顺序图:描述对象之间的交互,其中循环,选择等复杂交互使用序列片段表示。对象之间的消息类型包括同步消息/异步消息/返回消息/参与者创建消息/参与者销毁消息。 其中交互概览图、定时图、顺序图(序列图)、通信图(协作图)均被称为交互图。除此以外,动态模型还包括状态图和活动图等。 https://blog.csdn.net/HL_LOVE_C/article/details/142323515
2.异步消息传递异步消息通信机制mob64ca13f6bbea的技术博客异步消息传递 异步消息通信机制, 一、同步与异步概念:同步和异步关注的是消息通信机制(synchronouscommunication/asynchronouscommunication)。解释:涉及到IO通知机制;所谓同步,就是发起调用后,被调用者处理消息,必须等处理完才直接返回结果,没处理完之前是不https://blog.51cto.com/u_16213577/10541200
3.消息中间件第八讲:消息队列RocketMQ版实战集群及原理同步刷盘:消息被写入内存的PAGECACHE,返回写成功状态,当内存里的消息量积累到一定程度时,统一触发写磁盘操作,快速写入 。吞吐量高,当磁盘损坏时,会丢失消息 异步刷盘:消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,给应用返回消息写成功的状态。吞吐量低,但https://developer.aliyun.com/article/1352571
4.UML图详解(七)——交互图(时序图与协作图)对象的行为也称为消息(Message),通常当一个对象调用另一个对象中的行为时,即完成了一次消息传递。 2.表示方式 在生命线间的带有实心箭头表示消息 3.消息命名 信号或消息名(参数:参数类型):返回值 4.简单消息、同步消息、异步消息 消息分为简单消息(Simple Message)、同步消息(Synchronous Message)和异步消息(Asynchhttps://www.360doc.cn/article/9824753_689086442.html
5.下列选项属于消息的类型的是()下列选项属于消息的类型的是( ) A. 同步消息 B. 返回消息 C. 异步消息 D. 简单消息 E. 交互消息 题目标签:消息选项列选如何将EXCEL生成题库手机刷题 如何制作自己的在线小题库 > 手机使用 分享 反馈 收藏 举报 参考答案: A B C D 复制 纠错 举一反三 下列哪些方面属于数据库加密技术的功能https://www.shuashuati.com/ti/bd97b5557b5547b5a61600aa31c9b1a0.html
6.第八届全国职工职业技能大赛(网络和信息安全管理员)山西选拔赛63.“用户信息”可以理解为在用户使用产品或者服务过程中收集的信息构成用户信息,包括() A、IP地址 B、用户名和密码C、上网时间 D、Cookie信息 20 答案:ABCD 64.目前对MD5,对SHA1算法的攻击描述错误的是: A、能够构造出两个不同的消息,这两个消息产生了相同的消息摘要 B、对于一个已知的消息摘要,能够构造出https://max.book118.com/html/2024/0804/8036015104006116.shtm
7.UML答案214、简述顺序图中消息的分类 答:可分为同步消息、异步消息、返回消息三类。 同步消息:调用某个操作和方法。 异步消息:表示通信信号的非嵌套控制流,该信号异步激活操作。 返回消息:表明该消息从某个消息调用返回 ●必要时为每个消息附加上前置条件和后置条件。 ●调整图的布局,尽力减少线的交叉,使图尽可能地直观、https://m.360docs.net/doc/4a0ad13a3968011ca3009141.html
8.消息队列RocketMQ顺序消息消息队列RocketMQ功能特性用户购买商品生成订单为例,此时若以普通消息发送,则下游订单系统可能消息消费顺序混乱,例如订单出库先执行,后生成订单,从而系统数据不正确。顺序消息就能解决此问题,上游系统发送顺序消息,下游订单系统在同一消费组,会依次按顺序消费,执行相应的逻辑。 典型场景二: 数据实时增量同步 https://ecloud.10086.cn/op-help-center/doc/article/67007
9.消息队列(mq)是什么?有新消息写入,两个 follower 分区会同步 master 变更。两个 Consumer 分别从不同的 master 分区获取https://www.zhihu.com/question/54152397/answer/2654914176
10.3分钟白话RocketMQ系列——如何保证消息不丢失普通有序消息:发送普通有序消息,通过指定「消息筛选器selector」,动态决定发送哪个队列。异常默认不重试,可以用户自己重试,并发送到其他队列。 严格有序消息:发送严格有序消息,通过指定队列,保证严格有序,异常默认不重试。 消息发送模式: 同步:调用发送消息方法后,同步阻塞,直到返回SendResult。配置retryTimesWhenSendFaihttps://blog.itpub.net/70024923/viewspace-2983505/
11.计算机网络HTTP(Hyper Text Transfer Protocol)协议: 又称为超文本传输协议,HTTP由一个客户端和一个服务端来实现,运行在不同端系统中的客户端和服务端通过交换HTTP报文进行会话,而HTTP定义了这些报文的结构及进行报文交换时的方式。 HTTP是在TCP协议上运行的,客户端发起HTTP 请求,服务器做出响应处理后,返回HTTP 响应报文。 https://www.nowcoder.com/discuss/435756266465579008
12.大田县政法系统跨部门智慧执法办案管理平台建设项目服务类①价格项(F1×A1)满分为10分。 a.价格分采用低价优先法计算,即满足招标文件要求且投标价格最低的投标报价为评标基准价,其价格分为满分。其他投标人的价格分统一按照下列公式计算:投标报价得分=(评标基准价/投标报价)×100。因落实政府采购政策需进行价格扣除的,以扣除后的价格计算评标基准价和投标报价。 b.价格扣http://zfcg.cz.sm.gov.cn/upload/document/20211105/5f069fc764594cfea5cfa97e5b021347.html