应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

AndroidHandler面试题总结,学会这些还怕面试官?

  作者:xiangzhihong
  在Android面试中,有关Handler的面试是一个离不开的话题,下面我们就有关Handler的面试进行一个总结。1,Handler、Looper、MessageQueue、线程的关系一个线程只会有一个Looper对象,所以线程和Looper是一一对应的。MessageQueue对象是在newLooper的时候创建的,所以Looper和MessageQueue是一一对应的。Handler的作用只是将消息加到MessageQueue中,并后续取出消息后,根据消息的target字段分发给当初的那个handler,所以Handler对于Looper是可以多对一的,也就是多个Hanlder对象都可以用同一个线程、同一个Looper、同一个MessageQueue。
  综上,Looper、MessageQueue、线程是一一对应关系,而他们与Handler是可以一对多的。2,主线程为什么不用初始化Looper
  因为应用在启动的过程中就已经初始化了一个主线程Looper。每个java应用程序都是有一个main方法入口,Android是基于Java的程序也不例外,Android程序的入口在ActivityThread的main方法中,代码如下:初始化主线程LooperLooper。prepareMainLooper();。。。新建一个ActivityThread对象ActivityThreadthreadnewActivityThread();thread。attach(false,startSeq);获取ActivityThread的Handler,也是他的内部类Hif(sMainThreadHandlernull){sMainThreadHandlerthread。getHandler();}。。。Looper。loop();如果loop方法结束则抛出异常,程序结束thrownewRuntimeException(Mainthreadloopunexpectedlyexited);}
  可以看到,main方法中会先初始化主线程Looper,新建ActivityThread对象,然后再启动Looper,这样主线程的Looper在程序启动的时候就跑起来了。并且,我们通常认为ActivityThread就是主线程,事实上它并不是一个线程,而是主线程操作的管理者。3,为什么主线程的Looper是一个死循环,但是却不会ANR
  因为当Looper处理完所有消息的时候会进入阻塞状态,当有新的Message进来的时候会打破阻塞继续执行。
  首先,我们看一下什么是ANR,ANR,全名ApplicationNotResponding。当我发送一个绘制UI的消息到主线程Handler之后,经过一定的时间没有被执行,则抛出ANR异常。下面再来回答一下,主线程的Looper为什么是一个死循环,却不会ANR?Looper的死循环,是循环执行各种事务,包括UI绘制事务。Looper死循环说明线程没有死亡,如果Looper停止循环,线程则结束退出了,Looper的死循环本身就是保证UI绘制任务可以被执行的原因之一。
  关于这个问题,我们还可以得到如下的一些结论:真正会卡死的操作是在某个消息处理的时候操作时间过长,导致掉帧、ANR,而不是loop方法本身。在主线程以外,会有其他的线程来处理接受其他进程的事件,比如Binder线程(ApplicationThread),会接受AMS发送来的事件在收到跨进程消息后,会交给主线程的Hanlder再进行消息分发。所以Activity的生命周期都是依靠主线程的Looper。loop,当收到不同Message时则采用相应措施,比如收到msgH。LAUNCHACTIVITY,则调用ActivityThread。handleLaunchActivity()方法,最终执行到onCreate方法。当没有消息的时候,会阻塞在loop的queue。next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,所以死循环也不会特别消耗CPU资源。4,Message是怎么找到它所属的Handler然后进行分发的
  在loop方法中,找到要处理的Message需要调用下面的一段代码来处理消息:msg。target。dispatchMessage(msg);
  所以是将消息交给了msg。target来处理,那么这个target是什么呢,通常查看target的源头可以发现:privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){msg。targetthis;returnqueue。enqueueMessage(msg,uptimeMillis);}
  在使用Hanlder发送消息的时候,会设置msg。targetthis,所以target就是当初把消息加到消息队列的那个Handler。5,Handler是如何切换线程的
  使用不同线程的Looper处理消息。我们知道,代码的执行线程,并不是代码本身决定,而是执行这段代码的逻辑是在哪个线程,或者说是哪个线程的逻辑调用的。每个Looper都运行在对应的线程,所以不同的Looper调用的dispatchMessage方法就运行在其所在的线程了。6,post(Runnable)与sendMessage有什么区别
  我们知道,Hanlder中发送消息可以分为两种:post(Runnable)和sendMessage。首先,我们来看一下源码:publicfinalbooleanpost(NonNullRunnabler){returnsendMessageDelayed(getPostMessage(r),0);}privatestaticMessagegetPostMessage(Runnabler){MessagemMessage。obtain();m。callbackr;returnm;}publicfinalbooleansendMessage(NonNullMessagemsg){returnsendMessageDelayed(msg,0);}
  可以看到,post和sendMessage的区别就在于,post方法给Message设置了一个callback回调。那么,那么这个callback有什么用呢?我们再转到消息处理的方法dispatchMessage中看:publicvoiddispatchMessage(NonNullMessagemsg){if(msg。callback!null){handleCallback(msg);}else{if(mCallback!null){if(mCallback。handleMessage(msg)){return;}}handleMessage(msg);}}privatestaticvoidhandleCallback(Messagemessage){message。callback。run();}
  可以看到,如果msg。callback不为空,也就是通过post方法发送消息的时候,会把消息交给这个msg。callback进行处理;如果msg。callback为空,也就是通过sendMessage发送消息的时候,会判断Handler当前的mCallback是否为空,如果不为空就交给Handler。Callback。handleMessage处理。
  所以post(Runnable)与sendMessage的区别就在于后续消息的处理方式,是交给msg。callback还是Handler。Callback或者Handler。handleMessage。7,Handler如何保证MessageQueue并发访问安全的
  循环加锁,配合阻塞唤醒机制。我们发现,MessageQueue其实是【生产者消费者】模型,Handler不断地放入消息,Looper不断地取出,这就涉及到死锁问题。如果Looper拿到锁,但是队列中没有消息,就会一直等待,而Handler需要把消息放进去,锁却被Looper拿着无法入队,这就造成了死锁,Handler机制的解决方法是循环加锁,代码在MessageQueue的next方法中:Messagenext(){。。。for(;;){。。。nativePollOnce(ptr,nextPollTimeoutMillis);synchronized(this){。。。}}}
  我们可以看到他的等待是在锁外的,当队列中没有消息的时候,他会先释放锁,再进行等待,直到被唤醒。这样就不会造成死锁问题了。8,Handler的阻塞唤醒机制是怎么实现的
  Handler的阻塞唤醒机制是基于Linux的阻塞唤醒机制。这个机制也是类似于handler机制的模式。在本地创建一个文件描述符,然后需要等待的一方则监听这个文件描述符,唤醒的一方只需要修改这个文件,那么等待的一方就会收到文件从而打破唤醒。
  参考:Linux的阻塞唤醒机制9,什么是Handler的同步屏障
  所谓同步屏障,其实就是一个Message,只不过它是插入在MessageQueue的链表头,且其targetnull。而Message加急消息就是使用同步屏障实现的。同步屏障用到了postSyncBarrier()方法。publicintpostSyncBarrier(){returnpostSyncBarrier(SystemClock。uptimeMillis());}privateintpostSyncBarrier(longwhen){synchronized(this){finalinttokenmNextBarrierToken;finalMessagemsgMessage。obtain();msg。markInUse();msg。whenwhen;msg。arg1token;Messageprevnull;MessagepmMessages;把当前需要执行的Message全部执行if(when!0){while(p!nullp。whenwhen){prevp;pp。next;}}插入同步屏障if(prev!null){invariant:pprev。nextmsg。nextp;prev。nextmsg;}else{msg。nextp;mMessagesmsg;}returntoken;}}
  可以看到,同步屏障就是一个特殊的target,即targetnull,我们可以看到他并没有给target属性赋值,那这个target有什么用呢?Messagenext(){。。。阻塞时间intnextPollTimeoutMillis0;for(;;){。。。阻塞对应时间nativePollOnce(ptr,nextPollTimeoutMillis);对MessageQueue进行加锁,保证线程安全synchronized(this){finallongnowSystemClock。uptimeMillis();MessageprevMsgnull;MessagemsgmMessages;1if(msg!nullmsg。targetnull){同步屏障,找到下一个异步消息do{prevMsgmsg;msgmsg。next;}while(msg!null!msg。isAsynchronous());}if(msg!null){if(nowmsg。when){下一个消息还没开始,等待两者的时间差nextPollTimeoutMillis(int)Math。min(msg。whennow,Integer。MAXVALUE);}else{获得消息且现在要执行,标记MessageQueue为非阻塞mBlockedfalse;2一般只有异步消息才会从中间拿走消息,同步消息都是从链表头获取if(prevMsg!null){prevMsg。nextmsg。next;}else{mMessagesmsg。next;}msg。nextnull;msg。markInUse();returnmsg;}}else{没有消息,进入阻塞状态nextPollTimeoutMillis1;}当调用Looper。quitSafely()时候执行完所有的消息后就会退出if(mQuitting){dispose();returnnull;}。。。}。。。}}
  我们重点看一下关于同步屏障的部分代码。if(msg!nullmsg。targetnull){同步屏障,找到下一个异步消息do{prevMsgmsg;msgmsg。next;}while(msg!null!msg。isAsynchronous());}
  如果遇到同步屏障,那么会循环遍历整个链表找到标记为异步消息的Message,即isAsynchronous返回true,其他的消息会直接忽视,那么这样异步消息,就会提前被执行了。同时,,同步屏障不会自动移除,使用完成之后需要手动进行移除,不然会造成同步消息无法被处理。10,IdleHandler的使用场景
  前面说过,当MessageQueue没有消息的时候,就会阻塞在next方法中,其实在阻塞之前,MessageQueue还会做一件事,就是检查是否存在IdleHandler,如果有,就会去执行它的queueIdle方法。
  IdleHandler看起来好像是个Handler,但他其实只是一个有单方法的接口,也称为函数型接口。publicstaticinterfaceIdleHandler{booleanqueueIdle();}
  事实上,在MessageQueue中有一个List存储了IdleHandler对象,当MessageQueue没有需要被执行的Message时就会遍历回调所有的IdleHandler。所以IdleHandler主要用于在消息队列空闲的时候处理一些轻量级的工作。
  因此,IdleHandler可以用来进行启动优化,比如将一些事件(比如界面view的绘制、赋值)放到onCreate方法或者onResume方法中。但是这两个方法其实都是在界面绘制之前调用的,也就是说一定程度上这两个方法的耗时会影响到启动时间,所以我们可以把一些操作放到IdleHandler中,也就是界面绘制完成之后才去调用,这样就能减少启动时间了。11,HandlerThread使用场景
  首先,我们来看一下HandlerThread的源码:publicclassHandlerThreadextendsThread{Overridepublicvoidrun(){Looper。prepare();synchronized(this){mLooperLooper。myLooper();notifyAll();}Process。setThreadPriority(mPriority);onLooperPrepared();Looper。loop();}
  可以看到,HandlerThread是一个封装了Looper的Thread类,就是为了让我们在子线程里面更方便的使用Handler。这里的加锁就是为了保证线程安全,获取当前线程的Looper对象,获取成功之后再通过notifyAll方法唤醒其他线程,那哪里调用了wait方法呢?答案是getLooper方法。publicLoopergetLooper(){if(!isAlive()){returnnull;}Ifthethreadhasbeenstarted,waituntilthelooperhasbeencreated。synchronized(this){while(isAlive()mLoopernull){try{wait();}catch(InterruptedExceptione){}}}returnmLooper;}最后
  在这里就还分享一份由大佬亲自收录整理的学习PDF架构视频面试文档源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取

内蒙古额济纳旗胡杨林自驾游10月8日3车14人从杭州出发,奔向神往以久网红景区,单程约3400公里,当日抵达豫鲁苏皖四省交会地商丘市。游玩了商丘古城、水亭湖,打卡了华商鼻祖钱商广场。途经开封入园漫步在卞……刘思齐32岁含泪改嫁,婚后生4子,夫妻以长子名字纪念毛岸英用千千万万个相惜执手的情意凝结,年轻的他们方可在那个革命年代中喜结连理;再用满腔热血的刚毅志气战斗,革命年代独有的红色印迹就熔铸到了历史的车轮里,不可磨灭,更不可忘记。革命中的……新帅接手上港后,这位王牌将第一个被重用,曾在欧冠豪门踢主力上港队在上赛季中超和足协杯上都表现不佳,球队依然是没有拿到梦寐以求的冠军。所以,上港就会进行一系列调整,包括在主教练会请大牌名帅加西亚执教。加西亚是一位非常有水平的教练,他曾经……我国当今保存最完好的北宋城,竟在江西第二大城市,你去过吗?郁孤台下清江水,中间多少行人泪。这是宋代文武之才辛弃疾《菩萨蛮书江西造口壁》中的一句词,这句词也成为了本次的目的地,江南宋城。一座赣州城,半部宋代史。赣州,别称虔州,江西南大门……学习,到了只拼天赋的时代了?如今,天才是99的汗水1的灵感的后半句是家喻户晓的名言,许多家长对于后半句的重视程度甚至超过了前半句。许多所谓教育博主的营销号的内容和留言中,充斥着这样这样的言论:没有天赋,努……各族人民幸福安康伟大祖国繁荣昌盛大年初一,滑雪爱好者在新疆阿勒泰市将军山滑雪场滑雪。阿尔达克拜斯汗摄(影像中国)大年初一,游人在江苏苏州博物馆山水庭园畅游。王建中摄(影像中国)天南地北、城市乡村,……马赫是什么意思?1马赫速度到底有多快?相当于每小时多少公里?综述对于军事爱好者来说,马赫这个词汇并不是一个陌生的概念,在许多军事新闻中,我们都能看到所谓达到3。1,3。3马赫的战斗机,听上去就十分的高大上。然而,对于了解并不……冷知识揭穿生活中,人们长期相信着的7个错误的科学知识生活中有很多的知识可以说在不断的口口相传之中,例如,你可能听说过给婴儿听古典音乐可以提高孩子智力,老鼠喜欢吃奶酪。但是,如果小编告诉你,这一切根本就不符合事实,你是否会感到吃惊……一大批最猛的国产机型要来了,今年要换手机的别急,再等等有换机需求的先不要急,下半年迎来一波新机潮,这几款手机即将发布,有你喜欢的吗?第一款:魅族19Pro搭载天玑9000处理器,天玑9000基于台积电4nm制程工艺,C……28岁的武大靖长着50岁的脚永远不要低估拼搏的力量天才是百分之一的灵感加上百分之九十九的汗水,这句名言如今在运动员武大靖身上,再次得到验证。一档电视节目曾经展示武大靖的脚:因为常年穿着冰刀鞋训练,足部严重变形,28岁的他,长了……数字藏品风刮到金融圈,值得入手吗?作者陈畅编辑田晏林经历过炒起炒落的数字藏品,正在成为各大机构、银行的抢客新载体。所谓数字藏品,是指使用区块链技术对应特定作品、艺术品生成的唯一数字凭证,在保护……2021德杯小组赛结束UP全胜获得八强最后一个席位2021年德玛西亚杯昨天结束了全部的小组赛的比赛。C组最终的胜者是UP战队。再加上A组的TES、B组的iG还有D组的OMG,2021德玛西亚杯八强也出炉。12月22日14点就要……
双11大家电怎么选?三星这几款,优惠政策太给力由于前几个月新房刚装修完毕,所以今年双11,电视、冰箱、洗衣机等这些家用电器才是我的目标。这些大家电样样都不便宜呀!因此,我特意等到双11才入手,这样就能够为我省一大笔开支,想……浓眉轰3415威少14117!湖人6人上双力克马刺甜瓜首发1北京时间11月15日,湖人主场114106力克马刺,浓眉轰下34分15篮板。湖人全队6人得分上双。沃格尔将安东尼和霍顿塔克放入首发。湖人最多领先14分,但末节被对手缩小分差。湖……每天,给自己一个开心的理由丰子恺先生说:既然无处可逃,不如喜悦;既然没有净土,不如静心;既然没有如愿,不如释然。生活总是不完美的,与其一味较劲、纠缠,不如勇敢面对,淡然处之,与自己和解,与生活和解……分享一个小故事故事前景:有友友提问:(五十岁怀孕了,老公很开心,个人身体健康,家庭条件优越,可儿子反对,该生吗?)看到这个问题时我第一反应,胡扯!四十岁想怀孕生子都很困难好不好?直到看……女神惠若琪郎平最喜欢的弟子,艺术照引争议,相亲找到真命天子近期,前女排队长惠若琪在微博上晒出一家三口的照片,为女儿出生100天庆祝,画面十分温馨,照片里惠若琪与老公狂秀恩爱,怎么看这个孩子都有点多余的感觉。1991年出生的惠若琪……华为作为5G设备制造商之一,为何刚发出来的新手机为何不支持5华为有多艰难呢,任正非曾经公开承认过,说下一个倒下的一定是华为,而今天的华为,面对的又是一个怎样的内忧外患呢,外部环境那是被围追堵截,据步维艰,甚至华为作为五g设备的制造商之一……写代码写简历写方案它啥都会!它会给生活带来什么改变?导读继去年12月以最快速度5天突破百万用户之后,人工智能聊天机器人ChatGPT又创造了一个新的历史记录,发布两个月后,月活用户达到了1亿,成为史上蹿红最快的应用。它能够……重温闯关东命好的女人,都有一种百折不弯的韧劲《闯关东》这部电视剧虽然是部老片子了,但它真的是百看不腻,尤其是里面那些个性鲜明的人物形象,更是让人记忆犹新。就比如老朱家的大儿媳妇那文,大椿就非常喜欢,如果你看懂了她的生活之……印度赛李诗沣爆冷淘汰李梓嘉,陈雨菲携手何冰娇挺进八强北京时间1月20日,2023年世界羽联世界巡回赛超级750级别的印度公开赛结束第二轮比赛,中国队有多人进入八强。李诗沣三局力挫2号种子李梓嘉闯入八强,陈雨菲和何冰娇携手晋级。在……玄学圈里的阴与阳因为近年在关注某个小众圈子的事情,也关注了很多的大V,曾经我也很喜欢看他们发的内容,但是最近吃瓜了很多事情,也眼睁睁看着这些大V从粉丝众多到被锤,被喷。这些都让我改变了我曾经的……意外!国安王牌飞翼突然归队,离队已有1年,有望在足协杯上亮相作为昔日的中超豪门球队,北京国安在本赛季的日子非常不好过。受到国际疫情影响,目前国安队的4名外援都已经与俱乐部解约,而唯一留在队中的外援前锋巴坎布也很有可能不会再回到俱乐部。……不到3个月外来媳妇本地郎核心人物相继离世《外来媳妇本地郎》相信广东的朋友都不陌生,即使没有看过,也听过剧名。自2000年首播以来,已陪伴大家走过23个年头了,虽然是一部本土的情景家庭喜剧,超过4000集的拍摄在全国已……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网