Handler(异步消息处理机制)所在的主线程里面既然是无限循环为什么不阻塞?

2022-04-22 22:17:01 +08:00
 nnegier
这个在成都面试感觉问到的有点多呀,所以发上来讨论一下。

首先我个人没有直接回答这个问题,我个人觉得这个是会阻塞的,就是 ANR ,在一些方法里做了耗时操作(主线程),所以我觉得这个问题怪怪的。后面我也有补充,屏幕亮着,每隔一段时间 16ms (现在应该不是固定值了,看屏幕刷新率)就会刷新一次屏幕(当然还有很多其它类型的消息,触摸消息按键消息以及我们自己的消息等),意思就是一直都会有消息进来处理的意思。说到这儿,貌似已经不言自明了,这个问题就被放一边儿了,换个问题问了。

只是这个问题真的是问题吗?都说问一个好问题很重要。这个感觉有些问题。
10643 次点击
所在节点    Android
21 条回复
lait
2022-04-22 22:52:55 +08:00
无限循环着代表 app 运行着,循环结束 app 就停止了呀。

这个循环里如果有消息就处理,没有消息的时候就等待消息。

等待消息的阻塞,和你在处理消息时耗时太长而引起 anr 不是一回事。
你需要补充下基础概念,先看看 anr 的定义,然后再逐步深入理解等待消息的阻塞的基本解释和底层原理。
nnegier
2022-04-22 23:12:46 +08:00
@lait 对于你回复的第一行,如果循环结束,应用就是停止了。ActivityThread#main{ ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");}。 可以好好看看源码。 关于剩下的解释我过会儿尝试解答下
zagfai
2022-04-22 23:17:50 +08:00
事件触发
nnegier
2022-04-23 01:03:49 +08:00
@lait 除开第一行(你第一行是错的),貌似和我的又有什么冲突呢,它没消息肯定就等待呀进入 sleep 状态不占用 CPU 时间片嘛,有消息了就唤醒嘛,所谓的消息驱动模式。你要是仔细看的话,我的意思就是这个。关于 anr ,我倒还没有关注过它的源码,但你可以看 https://developer.android.com/topic/performance/vitals/anr ,“如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。”
lait
2022-04-23 02:37:51 +08:00
那您是对的,我理解有偏差吧。也许可以再一起研究研究 epoll 机制。
GeruzoniAnsasu
2022-04-23 07:57:06 +08:00
1. 循环和阻塞根本就是两个维度的概念,循不循环跟一个调用阻塞不阻塞半毛钱关系都没有

2. 我看不懂原问题想表达什么

3. 世界上的 GUI ,基本原理全都是一样的,全都有一个处理事件的循环

4. 世界上所有的程序,如果一个调用「阻塞了」,那么背后的原理也全都是一样的,caller 线程 hang 在了一个等待区,内核或调度器不会让 CPU 执行到「等待结束」后面的代码







虽然我真的没法看懂你想讨论的原问题是什么,但下面这个问题:

如果消息循环收不到任何消息,会阻塞在「取消息」操作上吗

的答案是,取决于实现。从节省 cpu 时间的角度考虑完全可以让它阻塞住。


下面这个追加问题:

那么既然阻塞住了,为什么不会被检测出死循环 /无响应

的答案是,调度器或者卡死检测器完全可以检测这个线程是否停在了等待区上,是则不认为线程无响应,这个线程 /routine 仍然是随时可调度的,只是没机会调度而已。
卡死 /无响应的表现是这个线程超过了阈值时间仍然没有返回等待区。
比如「取消息」就可以包含 renew 无响应超时的操作。假设它不阻塞,那么反正返回 caller 前刷新超时时间就好,如果它是阻塞的,那么执行流会回到调度器或内核里,调度器知道这个线程已经「重回可调度状态」,则不对其设置无响应定时器。
如果调度器把执行流交给一个线程前先设置一个定时中断,等来的是中断触发而非线程主动交还执行流,说明被调度线程无响应 /超时了








ps. 所有观点来自 OS 基本原理,我根本没研究过 androi 系统机制
Cabana
2022-04-23 11:20:13 +08:00
基础有点差,就去补补
Cabana
2022-04-23 11:26:21 +08:00
@Cabana “个人觉得”、“我觉得”,“应该不是”,“感觉有些问题”。搞技术的不要想当然,Android 从 java 到 native 源码都是公开,不懂就多看看。
StrorageBox
2022-04-23 11:42:15 +08:00
你是想说的 block 机制吧


looper.loop()方法中开始循环调用 loopOnce()方法
loopOnce()调用,messageQueue.next()方法,这方法是会被 block 的。

- block 的原因呢
有两种,其 1 是消息队列是空的,其 2 是开启了同步屏障,而消息队列中没有异步消息。

- 解除 block 的原理也是这样,在 messageQueue.enqueueMessage() 中 1.添加新消息,2.添加异步消息(异步消息有两种添加方式,你可以从源码去了解一下)

- 解除 block 的方法或者叫唤醒的方法,走 native wake(),本质上呢是通过 pipe 向指定 fd 进行写入一个 char(代码在 native Looper 中 ,同时可以了解一下 pipe 的原理)

- 写入之后发生了什么,在 messageQueue.next()中首先调用 nativePollOnce()进入到 native Looper.poll_once(),进入到 native Looper.poll_inner() ,里面就能看到通过 epoll 去监听 fd 了(除了上面的指定 fd ,还含有多个 request 对应的 fd),如果有写入,就会有监听结果返回,Done 之后,block 便解除了。

这个过程细节很多,感兴趣可以了解一下 epoll ,select/poll ,requst/response queue ,native handler 。

结果就是,在 block 的时候,其实是有 epoll 去监听的。wake 之后就继续执行了。无限循环是为了保证线程不被结束,和 anr 是没有关系的。

谈到 anr ,还是要重新理解一下概念。anr 发生的根本原因,不是线程什么都不做,而是 dispatchMessage()的执行时间,确切的说是上一个 message 的 disptch 时间过长导致现在的 message 不能被及时处理。

我们看看 google 的定义"ANR 是一个问题,因为负责更新界面的应用主线程无法处理用户输入事件或绘制操作,引起用户的不满"。我觉得这个定义虽然没有把所有发生 anr 的情况罗列出来,但是很好的表达了 anr 的目的。

回到你的描述上,为什么不卡屏幕刷新。作为应用程序的屏幕刷新时机有两种,1.requestlayout 刷新,2.重绘刷新,这两种刷新的原理都是通过 Choreographer 发送异步消息(在 Choreographer 就能看到两种异步消息的添加方式了)。

纯手打,可能有些函数名有点出入。


那么问题来了,屏幕上的图像到底是谁绘制的?
nnegier
2022-04-23 12:23:06 +08:00
@Cabana 我是发上来讨论,我也怕打脸呀
nnegier
2022-04-23 12:50:10 +08:00
@Cabana 我基础差?不知你对基础的定义是什么,好吧,反正我的确有被一些初级开发难倒过,他们喜欢问一些库怎么用,希望你不是。Android 源码的确是公开的,但那也太多了吧,你都看了吗,不过笨蛋才会全看吧,那我们聚焦于 ANR 的具体实现吧,你能回答吗?我自己在看,可以给你聚焦一下,在 ActivityManagerService 里面能找到。另外你回复了两条,但什么都没回答,期待你可能的最新回复是有价值的
Cabana
2022-04-23 14:55:24 +08:00
@nnegier 这问题不是 Android 八股文么?一般问出来就是想看你有没有了解过过 epoll 机制。至于详细解释楼上都说的很详细了,你不听就没办法了。
Cabana
2022-04-23 14:57:35 +08:00
@Cabana 最后还是提醒你,搞混淆的一点:无限循环不是阻塞!这一点楼上的朋友也多次指出了。
Cabana
2022-04-23 15:03:20 +08:00
@nnegier 哦哦,我第一句话的表述可能不太清楚。有可能误会我在说你简单的基础知识差,这里我想表达的是是底层的基础知识,如有误会 sorry 。v 站本来就比较少人讨论 android 技术,希望不要给你留下不太好的印象。
zpxshl
2022-04-23 20:29:21 +08:00
循环和阻塞完全是 2 个概念。
unco020511
2022-04-24 09:33:04 +08:00
gui 程序本来就是一个事件 /消息处理模型,没有消息时,相当于你的应用没有需要处理的界面信息,又何来阻塞一说呢?
Geele
2022-04-24 11:21:20 +08:00
如果 Android 应用的界面线程处于阻塞状态的时间过长,会触发“应用无响应”(ANR) 错误。”

4L 的这句话理解不准确,应该是一些指定的事件处理时间超过其阈值会触发 ANR
nothingistrue
2022-04-24 13:46:40 +08:00
线程阻塞状态,与同步任务的阻塞性,是两个概念。二者在某些情况下是相似的,但更可能是对立的。无线循环,是自己主动阻塞并让出资源,以便于整体上的不阻塞。

再详细点我不想说,反正楼主也不是来问问题的。
shiguiyou
2022-04-24 17:20:19 +08:00
经典安卓面试题(狗头
fromzero
2022-04-26 14:11:25 +08:00
八股文 搜一下网上都是答案。-_-||

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/848675

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX