(转)关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。...

( 转载请务必标明出处:http://www.cnblogs.com/linguanh/, 本文出自:【林冠宏(指尖下的幽灵)的博客】)

 

 

前序

  本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补充:

    1,安卓 APP 启动过程,对于Activity 的 onCreate 等生命周期的函数为什么不会因为 Looper.loop()里的死循环卡死而永无机会执行。

    2,在 1 的基础上,View 的绘制到底是怎样完成的,它又为什么不会因为 Looper.loop()里的死循环卡死而永无机会刷新。

    3,网传的观点大概如下:

        1.handler机制是使用pipe来实现的

        2.主线程没有消息处理时阻塞在管道的读端

        3.binder线程会往主线程消息队列里添加消息,然后往管道写端写一个字节,这样就能唤醒主线程从管道读端返回,也就是说queue.next()会调用返回

        4.dispatchMessage()中调用onCreate, onResume
 
    4,子线程真的不能刷新 UI ?
  
  其次,最终的内容我将放到两张图片上面去展示出来,源码的分析这里将不再累赘去说。第一部分网上很多,第二部分网上零散,我是通过源码分析书籍总结出来的。
  下面的阐述中,将采用:先告知答案,再放直观图片,最后文字辅助解析的顺序。

 
解答第一个问题
 
  此部分的源码分析,网上很多,搜索关键字,ActivityThread,Activity 的 onCreate 在哪调用等。
 
  总结:Activity 的 生命周期函数都是在 Looper 里面的死循环中被 ActivityThread 内部的 Handler 的 handleMessage 入口调用的,本身在循环里面调用,也就不会被阻塞,在 onCreate 等函数里面发送一个 message 也是会到这里被处理掉,仍然互不影响。
 
  先上图,看不懂,结合后面文字看,或者源码。
   点我查看 PDF 图片

 

   文字解析,仅描述重点:
  APP 的启动过程很复杂,但是最终的入口会在 ActivityThread 类里面的 main 函数,在这个函数里面,首先会调用 Looper.prepare 目的是实例化一个 looper 对象,方便后续的主线程中的 handler 实例化获取并使用。
 
  因为 Handler 的消息发送和处理机制是基于 Looper 里面 MessageQueue 的,所以得先存在looper。
  然后是实例化一个自身对象,即是 new ActivityThead(),在这里面会进行内部的两个重要变量的初始化,就是后续的mAppThread Binder实例以及一个HHandler实例,当 H 发送或处理下消息的时候,使用的Looper就是上面实例化的。然后是它自身的 attach(...)函数,在内部进行 AMS(ActivityManagerSevice)和mAppThread Binder进程通讯者的绑定,即是AMS的attachApplication(...)的调用。
 
  随后调用AMS的attachApplicationLocked(...),在这个函数里面,将会进行第一次跨进程通讯,AMS运行在系统进程,而我们的APP是另外一个进程。此时在AMS里面会调用我们上面绑定的mAppThread binder对象的bindApplication(...)方法,触发BIND_APPLICATION消息,该消息由 H 来进行发送,也就是 sendMessage(...),此时消息会在 Looper 里面的 loop() 进行处理。
 
  像 Handler 源码一样,最后会在 H.handleMessage(...) 处理, 然后就是进入到对应的函数里面进行 Context 的初始化,Application开始初始化,并且调用它的 onCreate,等其他操作。
 
  上面为第一部分,接下来是第二部分。在AMS的attachApplicationLocked(...)函数里面,在触发了第一次进程通讯后,代码接着运行,会在里面进行第二次的进程通讯,首先是Activity的栈管理者之一ActivityStackSupervisor调用它的attachApplicationLocked(...),在里面调用 realStartActivityLocked(...),然后是正式发起第二次IPC,触发 LAUNCH_ACTIVITY 消息,一样是 H 发送和处理,处理处调用 performLaunchActivity(...),在这里面,根据一直传下来的信息,将会 new 一个 Actiivity,然后就是调用它的 onCreate,onStart。
 
 
第二个问题
  
  声明:此部分的内部十分地复杂!包括下面的图与文字解析在内,仅作抛砖引玉,是个人总结的大概流程。关于源码分析,网上很零散,十分建议看源码分析类书籍。
 

  总结:View 的底层绘制是基于Binder进程通讯触发,由底层 SurfaceFlinger 的工作线程中的事件机制,包含 handler ,looper,messageQueue 来进行接收、处理,它和 ActivityThread 的 Handler 没关系,即是与 ActivityThread 几乎无关,但是如果在ActivityThread 里面调用 View的相关函数,例如 handleMessage 的一个 setText(..),最终触发到View其它底层函数,它将会将这些信息发送到 SurfaceFlinger 的事件机制中去,被对应处理,最终刷新到界面。

   点我查看 PDF 图片

  
   文字解析,里面所有函数和变量都是底层C++代码 的。Android 的GUI系统,也是图形界面系统,其依赖于OpenGL,EGL 等函数库,同时Android硬件HAL层的接口Composer的直接使用者是SurfaceFlinger,SurfaceFlinger,对于OpenGL而言,是一个很重要 ”应用“,它依赖于OpenGL,EGL的函数库,并使用他们的API来进行图形的最终绘制。
  SurfaceFlinger 在启动时会先进行自己内部的一个工作线程实例化和运行,该线程在后面承担着整个的绘制事件流程,在运行该线程时,会先进行MessageQueue内部的 looper 和 handler 的实例化,然后再 Run,Run 内部启动了事件的循环。
 
  从这一刻开始,它将进入到 waitForEvent(...)方法,这里是个死循环,并在里面调用 waitMessage(...),waitMessage 里面将会调用 looper的pollOnce(...),该方法和 ActivityThread 的 loope() 内部的 next() 里面的 queue.next() 差不多,不同的是 pollOnce(...) 会调用 MessageQueue 内部的函数 eventReceiver(...) 。
 
  eventReceiver 内部将会对进程中的消息获取,如果有收到其它进程传过来的对应的VSync 消息,那么将会对其进行下一步的分发,就是 dispatchInvalidate(...) 或 dispatchRefesh(...),最后会进入到 handler 的 handlMessage,然后回调 SurfaceFlinger 的 onMessgeReceiver(...),内部再调用 handleMessageRefresh(...)。
 
  然后是 SurfaceFlinger 的 layer层对View改变的绘制,绘制结合 NativeWindow 和 FrameBuffer 的缓存技术,最终将结果呈现到终端。代码非常复杂。
  
 
 
第三个补充
 
  网上对于博文标题的这个问题的解析普遍是:见前序的第三点,这里要补充的是,如果是 View 的 UI 刷新,不会导致阻塞的原因是本文的第二个解释,View 的绘制与 Java 代码的looper无关,而是由底层 SurfaceFlinger 自身的事件处理机制处理的。对于第一个问题的解析,那么可以参考前序第三点的内容。
 
 
第四个问题
  
   如果您有耐心看到这里,非常感谢,可能有朋友会想起 Android 的另外一句名言,子线程不能刷新UI,这样是否和上面说的冲突呢?其实不然,看下下面的代码片段,它是源码里面限制我们在子线程刷新UI的。
  
复制代码
 1 void checkThread() {
 2 
 3         if (mThread != Thread.currentThread()) {
 4 
 5             throw new CalledFromWrongThreadException(
 6 
 7                     "Only the original thread that created a view hierarchy can touch its views.");
 8 
 9         }
10 
11 }
复制代码

   代码第三行,其中 mThread 是创建 ViewRootImpl 的线程,而ViewRootImpl是在主线程中创建的,所以,我们习惯地称它为主线程,mThread和当前代码运行的线程来做了个等式运算,相同就出错,也就是说,并不是子线程不能刷新UI,准确来说,是发送进行 UI 刷新消息的消息,因为真正的底层刷新也不是当前 APP 的主线程。而是限制了,如果当ViewRootImpl是由子线程创造的,那么就可以在该子线程中发送更新UI的消息,自然地就能更新了,那么为什么限制呢?

  下面解析 引自知乎

  因为不光是gui,同样的道理在几乎所有编程领域里都是这样的,这背后是线程同步的开销问题。显然两个线程不能同时draw,否则屏幕会花;不能同时insert map,否则内存会花;不能同时write buffer,否则文件会花。需要互斥,比如锁。结果就是同一时刻只有一个线程可以做ui。那么当两个线程互斥几率较大时,或者保证互斥的代码复杂时,选择其中一个长期持有其他发消息就是典型的解决方案。所以普遍的要求ui只能单线程。

转载于:https://www.cnblogs.com/xswl/p/10135097.html

weixin_30622107
关注 关注
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android开发Looper.prepare()和Looper.loop()
01-04
 Looper用于封装了android线程的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Looper.loop()来使消息循环起作用,使用Looper....
Android为什么线程不会因为Looper.loop()死循环卡死
rainbow的博客
06-02 537
app程序入口线程准备好了消息队列 而根据Looper.loop()源码可知面是一个死循环在遍历消息队列取消息 而且并也没看见哪有相关代码为这个死循环准备了一个线程去运,但是线程却并不会因为Looper.loop()的这个死循环卡死,为什么呢? ...
Android 为什么线程不会因为 Looper.loop() 死循环卡死
闲暇部落
03-06 838
线程在处理一个事件或消息时,如果它花费了太多的时间(即超过了预定的时间片),操作系统会断它并将 CPU 资源分配给其他线程。在 Android 线程(通常被称为 UI 线程或事件分发线程)通过 Looper.loop() 方法进入一个无限循环,这个循环负责处理各种事件,如按钮点击、触摸事件、绘制请求等。只有当新的事件或消息到来时,它才会被唤醒并继续处理。综上所述,尽管 Looper.loop() 是一个死循环,但由于事件分发、消息处理、线程调度和非阻塞操作等机制,线程不会因此卡死
Android:Handler消息机制(四)——为什么线程不会因Looper.loop()死循环卡死
ZytheMoon的博客
04-29 2418
这个问题需要通过三方面来讲: 1.为什么线程不会因为Looper.loop()死循环卡死? 2.为什么线程一直在死循环不会占用大量CPU消耗? 3.那究竟是什么导致线程卡死? 一、为什么线程不会因为Looper.loop()死循环卡死? 首先理解“线程进入死循环”这个问题, 就是在循环体内具有一段可执行的子程序,由于for(; ;)的调度导致这段子程序持...
为什么Looper.loop()死循环不会导致ANR
热门推荐
罐v子的博客
02-28 1万+
为什么loop这个死循环会在线程执行,不会ANR么? 答:最开始Android的入口ActivityThread面的main方法,面有一个巨大的Handler,然后会创建一个线程的looper对象,这也是为什么直接在线程拿Handler就有Looper的原因,在其他线程是要自己Looper.prepare()的。 其实整个Android就是在一个Looper的loop循环的,整个An...
BravedBoy#YCBlogs#08.线程Looper的轮询死循环为何没阻塞线程1
07-25
1.技术博客汇总 2.开源项目汇总 3.生活博客汇总 4.喜马拉雅音频汇总 5.其他汇总
Android检测当前是否为线程最可靠的解决方法
01-05
如果在Android判断某个线程是否是线程?对于这个问题,你可能说根据线程的名字,当然这个可以解决问题,但是这样是最可靠的么?万一某天Google一下子将线程的名字改称其他神马东西呢。 方法揭晓 下面的方法是最...
Looper.loop为什么不会阻塞掉UI线程
jb_home的博客
02-16 595
要完全彻底理解这个问题,需要准备以下4方面的知识:Process/Thread,Android Binder IPC,Handler/Looper/MessageQueue消息机制,Linux pipe/epoll机制。 总结一下要有3个疑惑: 1.Android为什么线程不会因为Looper.loop()死循环卡死? 2.没看见哪有相关代码为这个死循环准备了一个线程去运? 3.Activity的生命周期这些方法这些都是在线程执行的吧,那这些生命周期方法是怎么实现在死循环体外能够
Looper的loop死循环为什么不会阻塞线程
冯旭的博客
09-22 1797
因为我们写的代码就是通过handler驱动起来的,我们activity的onCreate、onResume、onStop等等这些生命周期方法,包括我们的UI绘制的信号,这些UI绘制的事件都是通过Handler Looper循环内部发起的,来调用回调我们的各个Activity,各个Fragment等等这样的一些组件面的各个生命周期方法,我们的代码就是在循环面执行的,你说怎么会阻塞呢?你启动它,进入main方法,执行完所有的方法,也就会退出了,那么我们的应用程序你说总不能说执行完main方法就退出把?
关于Android为什么线程不会因为Looper.loop()死循环卡死
cmyperson的博客
02-21 1408
前序   本文将会把一下三个问题阐述清楚以及一个网上的普遍观点的补充:     1,安卓 APP 启动过程,对于Activity 的 onCreate 等生命周期的函数为什么不会因为 Looper.loop()死循环卡死而永无机会执行。     2,在 1 的基础上,View 的绘制到底是怎样完成的,它又为什么不会因为 Looper.loop()死循环卡死而永无机会刷新。
为什么Looper的Loop()方法不能导致线程卡死?
Android 开发架构
02-18 1589
关于 Handler 的问题已经是一个老生常谈的问题, 网上有很多优秀的文章讲解 Handler, 之所以还要拿出来讲这个问题, 是因为我发现, 在一些细节上面, 很多人还都似懂非懂, 面试的时候大家都能说出来一些东西, 但是又说不到点子上, 比如今天要说的这个问题: 为什么Looper 的 loop()方法不能导致线程卡死?? 先普及下 Android 消息机制 的基础知识: Android...
Looper.loop死循环为什么不会卡死
学会分享
10-29 3759
1) Android为什么线程不会因为Looper.loop()死循环卡死? 这涉及线程,先说说说进程/线程,进程:每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个
Android:Looper类,Looper.prepare()和Looper.loop()
cruise技术博客
08-23 3764
工作线程:在android应用程序,我们创建的Activity、Service、Broadcast等都是在线程UI线程)处理的,但一些比较耗时的操作,如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑创建一个工作线程(继承Thread类或者实现Runnable接口)来解决。 使用工作线程容易出现的问题:对于And
android线程卡死,为什么Looper的Loop()方法不能导致线程卡死
weixin_39612499的博客
05-27 220
Android 的消息机制涉及了四个类:Handler: 消息的发送者和处理着Message: 消息的载体MessageQueue: 消息队列Looper: 消息循环体其每一条线程只有一个消息队列MessageQueue, 消息的入队是通过 MessageQueue 的 enqueueMessage() 方法完成的, 消息的出队是通过Looper 的loop()方法完成的.Android 是...
Android 的looper死循环为什么不会卡死
分享Android开发知识
02-18 658
1、为什么是死循环 对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单死循环,无消息时会休眠。 1、原...
能写一个demo说明Looper.loop()的用法吗?
最新发布
09-11
Looper.loop()通常用于Android的消息循环机制,它与Looper.prepare()一起使用以在单独的线程处理Android的消息队列。这一个简单的示例: ```java // 创建一个新的线程(如AsyncTask或其他自定义线程) ...
写文章

热门文章

  • 解决Office 365应用程序无法正常启动(0X0000142) 23320
  • 电池充电方案总结 19603
  • 数码相机如何当做摄像头(图文并茂版) 19228
  • 软件测试(原书第2版中文)PDF版 14567
  • vue循环出来列表里面的列表点击click事件只对当前列表有效; 9999

最新文章

  • 第五周课程总结&试验报告(三)
  • Android -- SEGV_MAPERR,SEGV_ACCERR
  • Android.mk文件官方使用说明
2019年733篇
2018年794篇
2017年692篇
2016年542篇
2015年387篇
2014年307篇
2013年274篇
2012年225篇
2011年153篇
2010年112篇
2009年94篇
2008年79篇
2007年52篇
2006年44篇
2005年21篇
2004年7篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家广东室内雕塑玻璃钢北京周边商场美陈研发grc雕塑玻璃钢雕塑运城玻璃钢人物雕塑生产厂家瑞丽市玻璃钢雕塑设计厂家阿坝玻璃钢卡通雕塑实力厂家玻璃钢金鱼雕塑厂家马鞍山卡通玻璃钢雕塑定制玻璃钢仿真雕塑厂杭州玻璃钢雕塑摆件销售公司玻璃钢流油雕塑商场美陈制作规范石河子玻璃钢雕塑厂家玻璃钢古代人物雕塑设计方案阳江玻璃钢雕塑规定茂名美陈玻璃钢雕塑新余抽象玻璃钢雕塑制作新疆玻璃钢雕塑玻璃钢花盆简笔画植物玻璃钢工艺雕塑厂玻璃钢人物雕塑哪家买合肥商场入口美陈河南商场玻璃钢花盆寮步玻璃钢雕塑造型杭州道路玻璃钢花盆常见玻璃钢雕塑摆件哪里买河南卡通人玻璃钢雕塑摆件宽城区玻璃钢雕塑工程产品介绍玻璃钢雕塑卡通批发代理河北室内商场美陈销售厂家香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化